Promise.all並發限制


Promise.all概念

首先了解一下Promise.all

Promise.all可以將多個Promise實例包裝成一個新的Promise實例。同時,成功和失敗的返回值是不同的,成功的時候返回的是一個結果數組,而失敗的時候則返回最先被reject失敗狀態的值(第一次失敗就返回了,而不等待后續promise的狀態)。

Promise.all 可以保證,promises 數組中所有promise對象都達到 resolve 狀態,才執行 then 回調,而如果是有一個reject了,那就直接執行catch的代碼。

需要特別注意的是,Promise.all獲得的成功結果的數組里面的數據順序和Promise.all接收到的數組 順序是一致的。這帶來了一個絕大的好處:在前端開發請求數據的過程中,偶爾會遇到發送多個請求並根據請求順序獲取和使用數據的場景,使用Promise.all毫無疑問可以解決這個問題。

注意Promises數組的執行並不是通過Promise.all而是在我們初始化Promise的時候就執行了。可以看一下下面這個情況:

let a = new Promise((resolve,eject)=>{
    console.log(123);
    setTimeout(()=>resolve("Promise a"),1000)
})
let b = new Promise((resolve,eject)=>{
    console.log(222);
    setTimeout(()=>resolve("Promise b"),5000)
})
Promise.all([a,b]).then((res)=>console.log(res));
Promise.all([a,b]).then((res)=>console.log(res));

輸出結果為

 相當於,a和b兩個promise在創建的時候執行,我們只是通過Promise.all拿到結果而已,多次執行Promise.all只是多次獲取到結果,並不會執行promise數組。

 

Promise.all並發限制實現

⛲️ 場景:如果你都的promises 數組中每個對象都是http請求,你在瞬間發出幾十萬http請求(tcp連接數不足可能造成等待),或者堆積了無數調用棧導致內存溢出。這時,就需要考慮對Promise.all做並發限制

Promise.all並發限制指的是,每個時刻並發執行的promise數量是固定的,最終的執行結果還是保持與原來的Promise.all一致。

 

首先先看一下要實現的效果,有一個定時器,在1000,5000,3000,2000s之后輸出時間,每次並發為2。也就是先執行1000和5000,等到1000完成后,開始執行3000,以此類推直到所有任務完成(先實現這樣一個簡單的並發控制,並沒有對promise數組拿到結果,執行then)

 const timeout = i => new Promise(resolve => {
    console.log(i);
    setTimeout(() => resolve(i), i)
  });
 asyncPools(2, [1000, 5000, 3000, 2000], timeout)

1. asyncPools 接受三個參數(poolLimit, array, iteratorFn

poolLimit:並發限制

array:需要執行的並發數組

iteratorFn:具體執行的promise函數

 function asyncPools(poolLimit,array,fnInter) {
      let doing = [];
      let i =0;
      function pp(){
          if(i>=array.length) {
              return;
          }
          let e = fnInter(array[i++]); //初始化promise
          e.then(()=>doing.splice(doing.indexOf(e),1))  //完成之后從doing刪除
          doing.push(e); //放進doing列表
          if(doing.length>=poolLimit) {  //超出限制的話
            Promise.race(doing).then(pp); //監聽每完成一個就再放一個
          }
          else {  //否則直接放進去
            pp();
          }
      }
      pp();
  }

  思路:首先需要一個doing數組記錄正在執行的promise,因為需要不停的初始化並且放入doing數組,所以采用遞歸的形式。有一個變量i記錄初始化到array的第幾個了。---> 開始從array中初始化promise並且放入doing數組,因為promise完成之后需要從doing中刪除,所以注冊這個操作到初始化的promise中。--->那么下一次再初始化放入的時機就由poolLimit來決定,超出限制的話,使用promise.race來監聽doing列表有一個promise完成,就放入;沒有超出限制,直接放入。--->當array列表執行完畢也就是i>=array.length的時候結束操作。

2. 接下來是保存每一個promise狀態,在使用了並發asyncPools函數可以通過他的then方法拿到結果。所以我們對上面代碼進行了改進,增加一個ret來保存每一個初始化的promise,最后返回Promise.all(ret)拿到promise數組的結果。

使用示例:

asyncPools(2, [1000, 5000, 3000, 2000], timeout).then(res=>console.log(res));

第一步:存儲Promise數組,返回Promise.all(ret);

 function asyncPools(poolLimit,array,fnInter) {
    let doing = [];
    let i =0;
    let ret = [];  //結果數組
    function pp(){
        if(i>=array.length) {
            return Promise.all(ret); //返回結果
        }
        let e = fnInter(array[i++]); 
        e.then(()=>doing.splice(doing.indexOf(e),1))  
        doing.push(e); 
        ret.push(e); //放入ret數組
        if(doing.length>=poolLimit) {  
          Promise.race(doing).then(pp); 
        }
        else {  
          pp();
        }
    }
    pp();
}

 但是直接使用會報錯,因為內部是異步的,執行完之后拿到的結果其實是undefined,沒有then方法。

第二步:改寫函數的return,使得返回的是promise對象可以實現then的鏈式調用

function asyncPools(poolLimit,array,fnInter) {
    let doing = [];
    let i =0;
    let ret = [];
    function pp(){
        if(i>=array.length) {
            return Promise.resolve(); //最后一個resolve狀態,會進入外層返回Promise.then
        }
        let e = fnInter(array[i++]); 
        e.then(()=>doing.splice(doing.indexOf(e),1))  
        doing.push(e); 
        ret.push(e); 
        if(doing.length>=poolLimit) {  
          return Promise.race(doing).then(pp); //return返回
        }
        else { 
          return Promise.resolve().then(pp); //改寫一下保證then鏈式調用
        }
    }
    return pp().then(()=>Promise.all(ret)); //只有當array結束,最后一個resolve才會進入then
}

 至此我們的並發限制代碼就完成了!


 

下面的內容就是介紹一下和Promise.all經常一起出現的概念Promise.race了!

Promise.race(iterable) 方法返回一個 promise,一旦迭代器中的某個promise解決或拒絕,返回的 promise就會解決或拒絕。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM