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

来源: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
  • 示例項目結構 在 Visual Studio 中創建一個 WinForms 應用程式後,項目結構如下所示: MyWinFormsApp/ │ ├───Properties/ │ └───Settings.settings │ ├───bin/ │ ├───Debug/ │ └───Release/ ...
  • [STAThread] 特性用於需要與 COM 組件交互的應用程式,尤其是依賴單線程模型(如 Windows Forms 應用程式)的組件。在 STA 模式下,線程擁有自己的消息迴圈,這對於處理用戶界面和某些 COM 組件是必要的。 [STAThread] static void Main(stri ...
  • 在WinForm中使用全局異常捕獲處理 在WinForm應用程式中,全局異常捕獲是確保程式穩定性的關鍵。通過在Program類的Main方法中設置全局異常處理,可以有效地捕獲並處理未預見的異常,從而避免程式崩潰。 註冊全局異常事件 [STAThread] static void Main() { / ...
  • 前言 給大家推薦一款開源的 Winform 控制項庫,可以幫助我們開發更加美觀、漂亮的 WinForm 界面。 項目介紹 SunnyUI.NET 是一個基於 .NET Framework 4.0+、.NET 6、.NET 7 和 .NET 8 的 WinForm 開源控制項庫,同時也提供了工具類庫、擴展 ...
  • 說明 該文章是屬於OverallAuth2.0系列文章,每周更新一篇該系列文章(從0到1完成系統開發)。 該系統文章,我會儘量說的非常詳細,做到不管新手、老手都能看懂。 說明:OverallAuth2.0 是一個簡單、易懂、功能強大的許可權+可視化流程管理系統。 有興趣的朋友,請關註我吧(*^▽^*) ...
  • 一、下載安裝 1.下載git 必須先下載並安裝git,再TortoiseGit下載安裝 git安裝參考教程:https://blog.csdn.net/mukes/article/details/115693833 2.TortoiseGit下載與安裝 TortoiseGit,Git客戶端,32/6 ...
  • 前言 在項目開發過程中,理解數據結構和演算法如同掌握蓋房子的秘訣。演算法不僅能幫助我們編寫高效、優質的代碼,還能解決項目中遇到的各種難題。 給大家推薦一個支持C#的開源免費、新手友好的數據結構與演算法入門教程:Hello演算法。 項目介紹 《Hello Algo》是一本開源免費、新手友好的數據結構與演算法入門 ...
  • 1.生成單個Proto.bat內容 @rem Copyright 2016, Google Inc. @rem All rights reserved. @rem @rem Redistribution and use in source and binary forms, with or with ...
  • 一:背景 1. 講故事 前段時間有位朋友找到我,說他的窗體程式在客戶這邊出現了卡死,讓我幫忙看下怎麼回事?dump也生成了,既然有dump了那就上 windbg 分析吧。 二:WinDbg 分析 1. 為什麼會卡死 窗體程式的卡死,入口門檻很低,後續往下分析就不一定了,不管怎麼說先用 !clrsta ...
  • 前言 人工智慧時代,人臉識別技術已成為安全驗證、身份識別和用戶交互的關鍵工具。 給大家推薦一款.NET 開源提供了強大的人臉識別 API,工具不僅易於集成,還具備高效處理能力。 本文將介紹一款如何利用這些API,為我們的項目添加智能識別的亮點。 項目介紹 GitHub 上擁有 1.2k 星標的 C# ...