promise高級用法: 同步執行,並發控制,並發控制並獲取全部執行結果


原文鏈接:https://www.cnblogs.com/yalong/p/16038528.html

本文展示promise的三種用法

  1. promise實現異步代碼的同步執行
  2. promise控制大量請求的並發執行
  3. promise控制大量請求的並發執行,並獲取全部執行結果

一.Promise實現異步代碼的同步執行

場景描述:

有fn1 、fn2 、 fn3三個異步任務,要求必須先執行fn1,再執行fn2,最后執行fn3
且下一次任務必須要拿到上一次任務執行的結果,才能做操作
三個異步函數如下:

 // 異步函數fn1
 const fn1 = function () {
    return new Promise((resolve, reject) => {
    setTimeout(() => {
      resolve('111')
    }, 500)
    })
  }
  
 // 異步函數fn2
 const fn2 = (data) => {
  return new Promise((resolve, reject) => {
   resolve(data + '222')
  })
 }
  
 // 異步函數fn3
 const fn3 = (data) => {
  return new Promise((resolve, reject) => {
   setTimeout(function () {
    resolve(data + '333')
   }, 1000)
  })
 }

方法一:使用then的鏈式調用

  //鏈式調用
  fn1().then((data) => {
    return fn2(data)
  }).then((data) => {
    return fn3(data)
  })
  .then((data) => {
    console.log(data) // 111222333
  })

方法二:使用async await

 async function queue(arr) {
    let res = null
    for (let promise of arr) {
      res = await promise(res)
    }
    return await res // 這里的await可以去掉,因為已經是最后一步了
 }
 
 
 // 因為async返回返回的也是promise,所以可以使用then
 queue([fn1, fn2, fn3])
    .then(data => {
      console.log(data) // 111222333
    })

二. Promise並發控制

場景描述一

假設有100個ajax請求,每次發送3個請求,其中一個請求完畢,再加入新的請求,直到全部請求完畢
如果使用promsie.all,瀏覽器會瞬間發送100個請求,這樣可能會造成請求阻塞、頁面卡頓、甚至服務器崩潰,顯然不合理;
那么就需要設計一個方案

代碼實現

  // 這個就是每次執行的異步請求方法,參數不一樣
  const fn = (t) => {
    // 用setTimeout模擬異步請求
    return new Promise((resolve, reject) => {
        setTimeout(() => {
            console.log('任務完成', t, new Date());
            resolve({ t, date: new Date() });
        }, t * 1000);
    })
  };
  
   let arr = [1, 1, 1, 2, 2, 2, 3, 3, 3]


  /**
   * arrs 請求數據源數組
   * limit 是每次並行發起多少個請求
   * handleFn 就是異步處理函數
  */
  function limitQueueFn(arrs, limit, handleFn) {
    // 完成任務數
    let index = 0;
    // 第一次的時候 一次性執行 limit 個任務
    for (let i = 0; i < limit; i++) {
        run();
    }
    // 執行一個任務
    function run() {
        // 構造待執行任務 當該任務完成后 如果還有待完成的任務 繼續執行任務
        new Promise((resolve, reject) => {
            const value = arrs[index];
            index++; // 這個是同步操作
            // resolve 返回 promise
            resolve(handleFn(value))
        }).then(() => {
            if (index < arrs.length) {
                run()
            }
        })
    }
  };


  limitQueueFn(arr, 3, fn)

執行結果打印如下:

任務完成 1 2022-03-22T03:19:17.907Z
任務完成 1 2022-03-22T03:19:17.911Z
任務完成 1 2022-03-22T03:19:17.911Z
任務完成 2 2022-03-22T03:19:19.913Z
任務完成 2 2022-03-22T03:19:19.914Z
任務完成 2 2022-03-22T03:19:19.915Z
任務完成 3 2022-03-22T03:19:22.919Z
任務完成 3 2022-03-22T03:19:22.919Z
任務完成 3 2022-03-22T03:19:22.919Z

代碼分析

  • 首先並行發起limit個請求
  • 然后就是利用promise.then, 當請求完成之后,就去發起下一個請求
  • 使用變量 index 來統計執行了多少個請求,沒有執行就就一直執行

場景描述二

上面的代碼是可以實現並發請求控制了, 但是如果想獲取到全部請求執行完的結果,並且結果也要是有序的,跟 arrs 的順序一樣,這個怎么實現?

代碼實現

const fn = (t) => {
  // 用setTimeout模擬異步請求
  return new Promise((resolve, react) => {
      setTimeout(() => {
          console.log('任務完成', t, new Date());
          resolve({ t, date: new Date() });
      }, t * 1000);
  })
};

let arr = [1, 1, 1, 2, 2, 2, 3, 3, 3]

/**
 * arrs 請求數據源數組
 * limit 是每次並行發起多少個請求
 * handleFn 就是異步處理函數
*/
function limitQueueFn(arrs, limit, handleFn) {
  // 完成任務數
  let runningIndex = 0; // 這是正在執行的下標
  let finishedIndex = 0 // 這是已經執行完的下表
  let result = new Array(arrs.length).fill(0) // 建立一個空數組, 存儲結果
  return new Promise((resolveFn, rejectFn) => {
    // 第一次的時候 一次性執行 limit 個任務
    for (let i = 0; i < limit; i++) {
      run();
    }
    // 執行一個任務
    function run() {
        // 構造待執行任務 當該任務完成后 如果還有待完成的任務 繼續執行任務
        new Promise((resolve, reject) => {
            const value = arrs[runningIndex];
            runningIndex++; // 這個是同步操作
            resolve(handleFn(value))
        }).then((res) => {
            result[finishedIndex] = res
            finishedIndex++
            if (runningIndex < arrs.length) {
              run()
            } else { // 全部執行完畢
              resolveFn(result)
            }
        })
    }
  })
};

limitQueueFn(arr, 3, fn).then(res => {
  console.log('結果如下:')
  console.log(res)
})

執行結果打印如下:

任務完成 1 2022-03-22T03:18:10.420Z
任務完成 1 2022-03-22T03:18:10.426Z
任務完成 1 2022-03-22T03:18:10.426Z
任務完成 2 2022-03-22T03:18:12.428Z
任務完成 2 2022-03-22T03:18:12.430Z
任務完成 2 2022-03-22T03:18:12.430Z
任務完成 3 2022-03-22T03:18:15.435Z
任務完成 3 2022-03-22T03:18:15.436Z
任務完成 3 2022-03-22T03:18:15.436Z
結果如下:
[ { t: 1, date: 2022-03-22T03:18:10.426Z },
  { t: 1, date: 2022-03-22T03:18:10.426Z },
  { t: 1, date: 2022-03-22T03:18:10.426Z },
  { t: 2, date: 2022-03-22T03:18:12.429Z },
  { t: 2, date: 2022-03-22T03:18:12.430Z },
  { t: 2, date: 2022-03-22T03:18:12.430Z },
  { t: 3, date: 2022-03-22T03:18:15.436Z },
  { t: 3, date: 2022-03-22T03:18:15.436Z },
  { t: 3, date: 2022-03-22T03:18:15.436Z } ]
參考文章:

promise.all的實現
promise 實現並發控制


免責聲明!

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



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