reduce

除了依赖 async promise-fun 等工具库,最常用的队列操作就是 Array.prototype.reduce() 了:

let result = [1, 2, 5].reduce((accumulator, item) => {
  return accumulator + item;
}, 0); // <-- Our initial value.
 
console.log(result); // 8

最后一个值 0 是起始值,每次 reduce 返回的值都会作为下次 reduce 回调函数的第一个参数,直到队列循环完毕,因此可以进行累加计算。

那么将 reduce 的特性用在 Promise 试试:

function runPromiseByQueue(myPromises) {
  myPromises.reduce(
    (previousPromise, nextPromise) => previousPromise.then(() => nextPromise()),
    Promise.resolve()
  );
}

当上一个 Promise 开始执行(previousPromise.then),当其执行完毕后再调用下一个 Promise,并作为一个新 Promise 返回,下次迭代就会继续这个循环。

测试代码

const createPromise = (time, id) => () =>
  new Promise(solve =>
    setTimeout(() => {
      console.log("promise", id);
      solve();
    }, time)
  );
 
runPromiseByQueue([
  createPromise(3000, 1),
  createPromise(2000, 2),
  createPromise(1000, 3)
]);

得到的输出是:

promise 1
promise 2
promise 3

基本原理

Reduce 是同步执行的,在一个事件循环就会完成(更多请参考 精读《Javascript 事件循环与异步》),但这仅仅是在内存快速构造了 Promise 执行队列,展开如下:

new Promise((resolve, reject) => {
  // Promise #1
 
  resolve();
})
  .then(result => {
    // Promise #2
 
    return result;
  })
  .then(result => {
    // Promise #3
 
    return result;
  }); // ... and so on!

Reduce 的作用就是在内存中生成这个队列,而不需要把这个冗余的队列写在代码里!

 async/await

async function runPromiseByQueue(myPromises) {
  for (let value of myPromises) {
    await value();
  }
}

扩展阅读