記錄--前端實現併發請求限制

来源:https://www.cnblogs.com/smileZAZ/archive/2023/09/26/17730883.html
-Advertisement-
Play Games

這裡給大家分享我在網上總結出來的一些知識,希望對大家有所幫助 前言 前兩天我的新同事告訴我一個困擾著他的問題,就是低代碼平臺中存在很多模塊,這些模塊的渲染是由模塊自身處理的,簡言之就是組件請求了自己的數據,一個兩個模塊還好,要是一次請求了幾十個模塊,就會出現請求阻塞的問題,而且模塊的請求都特別大。 ...


這裡給大家分享我在網上總結出來的一些知識,希望對大家有所幫助

前言

前兩天我的新同事告訴我一個困擾著他的問題,就是低代碼平臺中存在很多模塊,這些模塊的渲染是由模塊自身處理的,簡言之就是組件請求了自己的數據,一個兩個模塊還好,要是一次請求了幾十個模塊,就會出現請求阻塞的問題,而且模塊的請求都特別大。

大量的併發請求會導致網路擁塞和帶寬限制。特別是當網路帶寬有限時,同時發送大量請求可能會導致請求之間的競爭,從而導致請求的響應時間延長。

因此模塊的載入就很不順暢。。。

為瞭解決這個問題我設計了一個關於前端實現併發請求限制的方案,下麵將詳細解釋這個併發請求限制的方案及實現源碼。

核心思路及簡易實現

一、收集需要併發的介面列表,併發數,介面調用函數。

二、遍歷arr,對於每一項,創建一個promise實例存儲到resArr中,創建的時候就已經開始執行了。

三、將創建的promise傳入的實例數組中,對於每一項的promise設置其then操作,並將其存儲到running數組中,作為執行中的標識。

四、當then操作觸發之後則將running中的對應這一項刪除,執行中的數組減一。

五、在遍歷的回調函數最後判斷當前是否超出閾值,當數量達到限制時開始批量執行,用await去處理非同步,處理完一個即跳走,重新往running中註入新的實例。

async function asyncLimit(limitNum, arr, fn) {
  let resArr = []; // 所有promise實例
  let running = []; // 執行中的promise數組
  for (const item of arr) {
    const p = Promise.resolve(fn(item)); // 遍歷arr,對於每一項,創建一個promise實例存儲到resArr中,創建的時候就已經開始執行了
    resArr.push(p);
    if (arr.length >= limitNum) {
      // 對於每一項設置其then操作,並將其存儲到running數組中,作為執行中的標識,當then操作觸發之後則將running中的對應這一項刪除,執行中的數組減一
      const e = p.then(() => running.splice(running.indexOf(e), 1));
      running.push(e);
      if (running.length >= limitNum) {
        // 當數量達到限制時開始批量執行,處理完一個即跳走,重新往running中註入新的實例
        await Promise.race(running);
      }
    }
  }
  return Promise.allSettled(resArr);
}
用例:
fn = (item) => { 
    return new Promise((resolve) => { 
        console.log("開始",item); 
        setTimeout(() => { console.log("結束", item); 
        resolve(item); 
     }, item) }); 
}; 
asyncLimit(2, [1000, 2000, 5000, 2000, 3000], fn)

註:但是這裡的實現太過簡陋,在真正的業務場景中往往沒有這樣使用場景,因此我對著段代碼封裝成一個符合前端使用的併發限制模塊,下麵是完整可用的代碼實現

完整實現及源碼用例

首先,讓我們來看一下代碼及用例:

let targetArray = []; // 目標調用數組
let resultArray = []; // 結果數組
let runningPromise = null; // 正在運行的 Promise
let limitNum = 0; // 最大併發數
let defaultFn = (value) => value; // 預設處理函數

 function asyncInit(limit, fn) {
  limitNum = limit;
  defaultFn = fn;
}

 async function asyncLimit(arr) {
  const promiseArray = []; // 所有 Promise 實例
  const running = []; // 正在執行的 Promise 數組

  for (const item of arr) {
    const p = Promise.resolve((item.fn || defaultFn)(item.value || item)); // 調用元素的處理函數
    promiseArray.push(p);

    if (arr.length >= limitNum) {
      const e = p.then(() => running.splice(running.indexOf(e), 1));
      running.push(e);

      if (running.length >= limitNum) {
        await Promise.race(running);
      }
    }
  }

  return Promise.allSettled(promiseArray);
}

 function asyncExecute(item) {
  targetArray.push(item);

  if (!runningPromise) {
    runningPromise = Promise.resolve().then(()=>{
      asyncLimit(targetArray).then((res) => {
        resultArray.push(...res);
        targetArray = [];
        runningPromise = null;
      });
    })
  }
}

這裡提供了一個併發模塊的文件。

簡單用例:

asyncInit(3, (item) => {
  return new Promise((resolve) => {
    console.log("開始",item);
    setTimeout(() => {
      console.log("結束", item);
      resolve(item);
    }, item)
  });
})

asyncExecute({value: 1000})
asyncExecute({value: 2000})
asyncExecute({value: 5000})
asyncExecute({value: 2000})
asyncExecute({value: 3000})

效果:

註:可以看到我們在使用的時候只需要先初始化最大併發數和預設調用函數,即可直接調用asyncExecute去觸發併發請求而且通過源碼我們可以看到如果 asyncExecute 的參數可以自定義調用函數,及傳入的對象中包含fn即可。

重點: 因為這些內容都被抽離成一個文件,所以我們可以導出asyncExecute這個函數然後業務側不同位置都可以通過這個函數去發起請求,這樣就能實現項目中所有請求的併發限制。

代碼解釋

這段代碼實現了一個前端併發限制的機制。讓我們逐步解釋其中的關鍵部分。

第一步

我們定義了一些變數,包括目標調用數組 targetArray 、結果數組 resultArray 、正在運行的 Promise runningPromise 、最大併發數 limitNum 和預設處理函數 defaultFn

第二步

定義 asyncInit 函數,用於初始化併發限制的最大數和預設處理函數。通過調用該函數,我們可以設置相關併發限制的參數。

第三步

然後,我們定義了 asyncLimit 函數,用於實現併發限制的核心邏輯。

在這個函數中,我們遍歷傳入的數組 arr ,對每個元素執行處理函數,並將返回的 Promise 實例存儲在 promiseArray 數組中。

同時,我們使用 running 數組來跟蹤正在執行的 Promise 實例。

如果當前正在執行的 Promise 實例數量達到最大併發數,我們使用 Promise.race 方法等待最先完成的 Promise,以確保併發數始終保持在限制範圍內。(這裡對應的就是核心思路及簡易實現中的代碼)

註:如果要實現非同步併發,我們只要保證我們的介面存在於傳入的數組即arr中即可。

第四步

定義 asyncExecute 函數,用於觸發非同步操作的執行。

當調用 asyncExecute 函數時,我們將目標元素添加到 targetArray 數組中,這個targetArray就是非同步並行的介面隊列,只要把這個傳入到asyncLimit中就能實現非同步並行

檢查是否有正在運行的 runningPromise
runningPromise的作用:
判斷當前是否已經有運行中的asyncLimit

如果有那麼我們只需要繼續往targetArray中加入數據即可,沿用之前的asyncLimit即之前的Promise 鏈。

如果沒有說明asyncLimit函數已經執行完了,我們要新開一個asyncLimit函數去完成我們的並行限制。調用 asyncLimit 函數來處理目標數組中的元素,並基於此創建一個新的 Promise 鏈。

處理完成後,我們將結果存儲在 resultArray 中,並重置目標數組和運行的 Promise。

總結

非同步並行邏輯交由asyncLimit處理即可。
使用上來說,就只需要使用到介面的時候,調用asyncExecutetargetArray 加數據就行,預設會直接執行 asyncLimit 並創建一個promise鏈。
當我們往裡面加一項promise鏈就會對應的多一項,當我們promise鏈執行完之後我們就會重置targetArrayrunningPromise
下次調用asyncExecute時,如果runningPromise不存在就重新走上面的邏輯,即直接執行 asyncLimit 並創建一個promise鏈,當runningPromise存在的情況下,每次使用asyncExecutetargetArray裡面push參數即可。

本文轉載於:

https://juejin.cn/post/7282733743910731833

如果對您有所幫助,歡迎您點個關註,我會定時更新技術文檔,大家一起討論學習,一起進步。

 


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

-Advertisement-
Play Games
更多相關文章
  • 1、Stream記憶體帶寬測試 Stream是業界主流的記憶體帶寬測試程式,測試行為相對簡單可控。該程式對CPU的計算能力要求很小,對CPU記憶體帶寬壓力很大。隨著處理器核心數量的增大,而記憶體帶寬並沒有隨之成線性增長,因此記憶體帶寬對提升多核心的處理能力就越發重要。Stream具有良好的空間局部性,是對TL ...
  • 前言 不想看可以跳過前言部分,教程在下幾章。 ​ 最新搬到新校園,寢室的校園網可使用網線連接。雖然撥號的寬頻賬號和密碼已經自動記錄,但啟動電腦並登入電腦時仍需要手動進入設置並點擊自動登錄,就像鞋子里的小石子,雖然腳不會出血,但就是難受。於是開始網上搜索教程win11自動撥號。結合了兩篇文章實現了開機 ...
  • MySQL 高級(進階) SQL 語句 use gy; create table location (Region char(20),Store_Name char(20)); insert into location values('East','Boston'); insert into loc ...
  • 一、背景 在預發環境中,由消息驅動最終觸發執行事務來寫庫存,但是導致MySQL發生死鎖,寫庫存失敗。 com.mysql.jdbc.exceptions.jdbc4.MySQLTransactionRollbackException: rpc error: code = Aborted desc = ...
  • 為了標識一段數據,通常我們會為其指定一個唯一id,比如利用MySQL資料庫中的自增主鍵。 但是當數據量非常大時,僅靠資料庫的自增主鍵是遠遠不夠的,並且對於分散式資料庫只依賴MySQL的自增id無法滿足全局唯一的需求。因此,產生了多種解決方案,如UUID,SnowFlake等。下文將介紹Vitess是... ...
  • 一直以來,大數據量一直是爆炸性增長,每天幾十 TB 的數據增量已經非常常見,但雲存儲相對來說還是不便宜的。眾多雲上的大數據用戶特別希望可以非常簡單快速的將文件移動到更實惠的 S3、OSS 上進行保存,這篇文章就來介紹如何使用 SeaTunnel 來進行到 OSS 的數據同步。 首先簡要介紹一下 Ap ...
  • 奇富科技(原360數科)是人工智慧驅動的信貸科技服務平臺,致力於憑藉智能服務、AI研究及應用、安全科技,賦能金融機構提質增效,助推普惠金融高質量發展,讓更多人享受到安全便捷的金融科技服務。作為國內領先的信貸科技服務品牌,累計註冊用戶數2億多。 奇富科技之前使用的是自研的任務調度框架,基於Python ...
  • 本篇作為 OPPO主題組件調試與預覽 文檔的補充,因為它真的很簡單而且太老,一些命令已發生變化😪 此圖片來自官網 一、調試前準備 1. PC 端下載 adb命令工具 下載 下載地址 https://adbdownload.com/,或從其他地方下載也可 解壓,放在你想放的文件夾下 配置環境變數 右 ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...