前端性能優化之控制請求併發數

来源:https://www.cnblogs.com/coder--wang/archive/2022/08/01/16540998.html
-Advertisement-
Play Games

在我們平時開發中,經常會遇到頁面數據初始化時,頻繁調同一個介面的情況。比如echarts項目中,一個頁面可能會有幾十張圖表,如果一個介面返回所有圖表數據的話,會造成用戶過長的等待時間,再者過多圖表同時渲染,也會給頁面增加壓力,造成卡頓的現象。 我們通常會讓每個圖表單獨調一個介面,入參不同,這樣更有利 ...


  在我們平時開發中,經常會遇到頁面數據初始化時,頻繁調同一個介面的情況。比如echarts項目中,一個頁面可能會有幾十張圖表,如果一個介面返回所有圖表數據的話,會造成用戶過長的等待時間,再者過多圖表同時渲染,也會給頁面增加壓力,造成卡頓的現象。

  我們通常會讓每個圖表單獨調一個介面,入參不同,這樣更有利於頁面快速渲染圖表,單個圖表請求到數據,立即渲染,不需要等待其他圖表。可理想很豐滿,現實很骨感,當伺服器配置過低,或者後端代碼性能較弱,會難以處理這些併發請求,介面調用越多,等待處理的時間可能就越長,甚至超過一次性返回所有數據的間。。。為瞭解決這種問題,緩解後端壓力,本篇將介紹前端來控制請求的併發數:

  先分析一波,假設我們需要重覆調用30次介面,並聯調用介面,服務端壓力較大,可能會造成響應時間過長。逐漸減少併發數,假設併發數為5的時候,伺服器處理速度最快,幾乎不受併發影響。

  針對這種情況,我們可以封裝介面請求方法,控制每次介面請求的併發數,將30次分解成:併發數為5,分6次請求。這樣的話,伺服器每次處理5次請求,資源釋放出來繼續處理下一批請求,從而解決併發擁堵問題~

初步構思:

class TaskQueue {
  constructor(max) {
    this.max = max;
    this.taskList = [];
  }

  addTask(task) {
    this.taskList.push(task);
  }
}

function createTask(i) {
  return () => {
    return new Promise((resolve, reject) => {
      setTimeout(() => {
        if (i == 4 || i == 15) {
          reject("出錯啦~");
        } else {
          resolve("成功呀~" + i);
        }
      }, 2000);
    });
  };
}

const taskQueue = new TaskQueue(5);

for (let i = 0; i < 30; i++) {
  const task = createTask(i);
  taskQueue.addTask(task);
}
for迴圈調用函數createTask()返回30個promise的非同步任務,任務隊列TaskQueue類返回一個實例,控制這30個非同步任務的併發,構造器中傳入併發數5。
接下來用TaskQueue實現控制併發:
class TaskQueue {
  constructor(max) {
    this.max = max; // 併發數
    this.min = 0;
    this.taskList = []; // 全部任務
    Promise.resolve().then(() => this.run()) // 等同步代碼(addTask)全部執行完成,再執行run
  }

  // 增加任務
  addTask(task) {
    this.taskList.push(task);
  }

  // 執行任務
  async run() {
    if (!this.taskList.length) return;
    const AsyncTasks = [];
    this.min = Math.min(this.max, this.taskList.length) // 當傳入的併發數大於任務數,取任務數, 反之取併發數
    // 根據併發數分組
    for(let i = 0; i < this.min; i++) {
       AsyncTasks.push(this.taskList.shift());
    }
    await this.handleTask(AsyncTasks); // 通過下麵遞歸,這裡將會有6個非同步任務串聯執行

    this.run(); // 遞歸
  }

  async handleTask(tasks) {
    // 返回promise處理非同步任務組
    return new Promise(resolve => {
      // 遍歷任務組,5個非同步任務並聯執行
      tasks.forEach(async (task, index) => {
        await task().then(res => {
          console.log(res);
        }).catch((err) => {
          console.log(err);
        }).finally(() => {
          index + 1 === this.min && console.log('===============================');
          index + 1 === this.min && resolve() // 最後一個任務resolve(),promise完成
        })
      })
    })
  }
}

function createTask(i) {
  return () => {
    return new Promise((resolve, reject) => {
      setTimeout(() => {
        if (i == 4 || i == 15) {  // 測試捕捉錯誤
          reject("出錯啦~");
        } else {
          resolve("成功呀~" + i);
        }
      }, 2000);
    });
  };
}

const taskQueue = new TaskQueue(5);

for (let i = 0; i < 30; i++) {
  const task = createTask(i);
  taskQueue.addTask(task);
}

試試效果:

 

 nice,至此,30次非同步任務,分6次完成,每次處理5個,大家可以在此基礎上拓展請求介面,並增加一些處理邏輯,歡迎留言探討~

 

腳踏實地行,海闊天空飛~

 

搜索

複製


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • mysql主從 1.主從原理 1.1 主從介紹 所謂mysql主從就是建立兩個完全一樣的資料庫,其中一個為主要使用的資料庫,另一個為次要的資料庫,一般在企業中,存放比較重要的數據的資料庫伺服器需要配置主從,這樣可以防止因資料庫伺服器宕機導致數據丟失,還能保證業務量太多、數據太多和訪問人數太多時服務的 ...
  • mysql 簡潔式安裝步驟 1. 設置全局變數 解壓mysql壓縮包到指定位置, 然後配置全局變數, 在 path 中添加全局變數, 值為 mysql 根目錄下 bin 目錄路徑, 比如: D:\code_space\environments\mysql-8.0.30\bin 然後保存即可 2. 配 ...
  • 常用命令 啟動redis服務(Windows) 在redis的目錄下執行命令: redis-server 啟動redis客戶端實例(Windows) 在redis的src目錄下執行命令: redis-cli 連接遠程redis伺服器:(Windows) redis-cli -h host -p po ...
  • 一、直播介紹 上期雅澤同學對ChengYing是什麼、有什麼樣的功能特性,如何快速入門做了介紹,本期海洋同學將會為大家分享ChengYing部署Hadoop集群實戰的相關內容,歡迎大家積极參与。 二、直播主題 ChengYing部署Hadoop集群實戰 三、直播時間 時間:2022年8月2日晚 19 ...
  • 我們都理解B+樹和Hash索引的區別有助於我們預測索引在不同的存儲引擎中是怎麼執行查詢的。 B+ TREE 索引特性B數是一種在資料庫索引中流行的樹數據結構。該結構始終保持排序,從而可以快速查找精確匹配。MySQL中使用的是B樹的一種變體,B+樹,這種類型的索引可用於大多數存儲引擎,例如InnoDB ...
  • 綠幕摳圖是影視製作過程中常見的技術手段,常用於視頻中摳除並替換背景,通過後期加工實現視頻剪輯製作的更多可能性。然而,綠幕摳圖技術製作成本費時費力,無法應用於日常生活。 華為視頻編輯服務近期上線目標分割能力,可通過AI智能摳圖精細化分割視頻中的目標物體,並且不局限於特定的物體類別,在主體明確、背景相對 ...
  • DAY01 電腦的介紹 特點: 1.可以進行數值計算,可以進行邏輯計算 2.具有存儲記憶功能 硬體:看得見,摸得著的 顯示器,主機,存儲器 軟體:看得見,摸不著的 系統軟體:操作系統:windows、Linux、UNIX等 應用軟體:各類app C/S架構和B/S架構 1.C/S架構:需要安裝 a ...
  • 字元串 字元串概述(個人理解字元串就是把一串字元連接在一起,而且他的值類型是常量,所以不能改變,返回值只能返回一個新的字元串) 字元串也是一個數據結構(串),將同樣的內容串在一塊。因為在對應的js裡面字元串屬於一個值類型(值類型是常量 常量是不能變)。字元串是不能改變的。結合昨天提到的數據結構裡面串 ...
一周排行
    -Advertisement-
    Play Games
  • 一、openKylin簡介 openKylin(開放麒麟) 社區是在開源、自願、平等和協作的基礎上,由基礎軟硬體企業、非營利性組織、社團組織、高等院校、科研機構和個人開發者共同創立的一個開源社區,致力於通過開源、開放的社區合作,構建桌面操作系統開源社區,推動Linux開源技術及其軟硬體生態繁榮發展。 ...
  • 簡介 Flurl是一個用於構建基於HTTP請求的C#代碼的庫。它的主要目的是簡化和優雅地處理網路請求(只用很少的代碼完成請求)。Flurl提供了一種簡單的方法來構建GET、POST、PUT等類型的請求,以及處理響應和異常。它還提供了一些高級功能,如鏈式調用、緩存請求結果、自動重定向等。本文將介紹Fl ...
  • 一:背景 1. 講故事 最近也挺奇怪,看到了兩起 CPU 爆高的案例,且誘因也是一致的,覺得有一些代表性,合併分享出來幫助大家來避坑吧,閑話不多說,直接上 windbg 分析。 二:WinDbg 分析 1. CPU 真的爆高嗎 這裡要提醒一下,別人說爆高不一定真的就是爆高,我們一定要拿數據說話,可以 ...
  • 剛開始寫文章,封裝Base基類的時候,添加了trycatch異常塊,不過當時沒有去記錄日誌,直接return了。有小伙伴勸我不要吃了Exception 其實沒有啦,項目剛開始,我覺得先做好整體結構比較好。像是蓋樓一樣。先把樓體建造出來,然後再一步一步的美化完善。 基礎的倉儲模式已經ok,Autofa ...
  • 框架目標 什麼是框架,框架能做到什麼? 把一個方向的技術研發做封裝,具備通用性,讓使用框架的開發者用起來很輕鬆。 屬性: 通用性 健壯性 穩定性 擴展性 高性能 組件化 跨平臺 從零開始-搭建框架 建立項目 主鍵查詢功能開發 綁定實體 一步一步的給大家推導: 一邊寫一邊測試 從零開始--搭建框架 1 ...
  • 大家好,我是沙漠盡頭的狼。 本方首發於Dotnet9,介紹使用dnSpy調試第三方.NET庫源碼,行文目錄: 安裝dnSpy 編寫示常式序 調試示常式序 調試.NET庫原生方法 總結 1. 安裝dnSpy dnSpy是一款功能強大的.NET程式反編譯工具,可以對.NET程式進行反編譯,代替庫文檔的功 ...
  • 在`Windows`操作系統中,每個進程的虛擬地址空間都被劃分為若幹記憶體塊,每個記憶體塊都具有一些屬性,如記憶體大小、保護模式、類型等。這些屬性可以通過`VirtualQueryEx`函數查詢得到。該函數可用於查詢進程虛擬地址空間中的記憶體信息的函數。它的作用類似於`Windows`操作系統中的`Task... ...
  • 背景介紹 1,最近有一個大數據量插入的操作入庫的業務場景,需要先做一些其他修改操作,然後在執行插入操作,由於插入數據可能會很多,用到多線程去拆分數據並行處理來提高響應時間,如果有一個線程執行失敗,則全部回滾。 2,在spring中可以使用@Transactional註解去控制事務,使出現異常時會進行 ...
  • 線程(thread)是操作系統能夠進行運算調度的最小單位。它被包含在進程之中,是進程中的實際 運作單位。一條線程指的是進程中一個單一順序的控制流,一個進程中可以併發多個線程,每條線 程並行執行不同的任務。 ...
  • 發現Java 21的StringBuilder和StringBuffer中多了repeat方法: /** * @throws IllegalArgumentException {@inheritDoc} * * @since 21 */ @Override public StringBuilder ...