當前位置: 妍妍網 > 碼農

用30行程式碼封裝一個工具,解決Promise的多並行問題

2024-06-02碼農

每日一學~

前言

大家好,我是林三心,用最通俗易懂的話講最難的知識點是我的座右銘,基礎是進階的前提是我的初心。

背景

提起 控制並行 ,大家應該不陌生,我們可以先來看看 多並行 ,再去聊聊為什麽要去控制它

多並行 一般是指多個異步操作同時進行,而執行的環境中資源是有限的,短時間內過多的並行,會對所執行的環境造成很大的壓力,比如前端的瀏覽器,後端的伺服器,常見的多並行操作有:

  • 前端的多個介面同時請求

  • 前端多條數據異步處理

  • Nodejs的多個數據操作同時進行

  • Nodejs對多個檔同時進行修改

  • 正是因為 多並行 會造成壓力,所以我們才需要去控制他,降低這個壓力~,比如我可以控制最大並行數是 3,這樣的話即使有100個並行,我也能保證最多同時並行的最大數量是 3

    程式碼實作

    實作思路

    大致思路就是,假設現在有 9 個並行,我設定最大並行為 3,那麽我將會走下面這些步驟:

  • 1、先定好三個坑位

  • 2、讓前三個並行進去坑位執行

  • 3、看哪個坑位並行先執行完,就從剩余的並行中拿一個進去補坑

  • 4、一直重復第 3 步,一直到所有並行執行完

  • Promise.all

    在進行多並行的時候,我們通常會使用 Promise.all ,但是 Promise.all 並不能控制並行,或者說它本來就沒這個能力,我們可以看下面的例子

    最後是同時輸出,這說明這幾個並行是同時發生的

    所以我們需要做一些改造,讓 Promise.all 執行 promises 時支持控制並行,但是我們改造的不應該是 Promise.all ,而是這一個個的 fetchFn

    期望效果

    實作 limitFn

    我們需要在函式內部維護兩個變量:

  • queue:佇列,用來存每一個改造過的並行

  • activeCount:用來記錄正在執行的並行數

  • 並聲明函式 generator ,這個函式返回一個 Promise,因為 Promise.all 最好是接收一個 Promise 陣列

    接下來我們來實作 enqueue 這個函式做兩件事:

  • 將每一個 fetchFn 放進佇列裏

  • 將坑位裏的 fetchFn 先執行

  • 假如我設定最大並行數為 2,那麽這一段程式碼在一開始的時候只會執行 2 次,因為一開始只會有 2 次符合 if 判斷,大家可以思考一下為什麽~

    一開始執行 2 次,說明這時候兩個坑位已經各自有一個 fetchFn 在執行了

    接下來我們實作 run 函式,這個函式是用來包裝 fetch 的,他完成幾件事情:

  • 1、將 activeCount++ ,這時候執行中的並行數 +1

  • 2、將 fetchFn 執行,並把結果 resolve 出去,說明這個並行執行完了

  • 3、將 activeCount--,這時候執行中的並行數 -1

  • 4、從 queue 中取一個並行,拿來補坑執行

  • 其實第 3、4 步,是在 next 函式裏面執行的

    完整程式碼

    const limitFn = (limit) => {
    const queue = [];
    let activeCount = 0;
    const next = () => {
    activeCount--;
    if (queue.length > 0) {
    queue.shift()();
    }
    };
    const run = async (fn, resolve, ...args) => {
    activeCount++;
    const result = (async () => fn(...args))();

    try {
    const res = await result;
    resolve(res);
    catch { }
    next();
    };
    const enqueue = (fn, resolve, ...args) => {
    queue.push(run.bind(null, fn, resolve, ...args));
    if (activeCount < limit && queue.length > 0) {
    queue.shift()();
    }
    };
    const generator = (fn, ...args) =>
    newPromise((resolve) => {
    enqueue(fn, resolve, ...args);
    });
    return generator;
    };








    這不是我寫的

    其實這是一個很出名的庫的源碼,就是 p-limit ,哈哈,但是重要嗎?知識嘛,讀懂了,它就是你的,到時跟面試官嘮嗑的時候,他哪知道是不是真的是你寫的~

    結語

    我是林三心

  • 一個待過 小型toG型外包公司、大型外包公司、小公司、潛力型創業公司、大公司 的作死型前端選手;

  • 一個偏前端的全幹工程師;

  • 一個不正經的金塊作者;

  • 逗比的B站up主;

  • 不帥的小紅書博主;

  • 喜歡打鐵的籃球菜鳥;

  • 喜歡歷史的乏味少年;

  • 喜歡rap的五音不全弱雞

  • 如果你想一起學習前端,一起摸魚,一起研究簡歷最佳化,一起研究面試進步,一起交流歷史音樂籃球rap,可以來俺的摸魚學習群哈哈,點這個,有7000多名前端小夥伴在等著一起學習哦 -->

    廣州的兄弟可以約飯哦,或者約球~我負責打鐵,你負責進球,謝謝~