Skip to content

串行执行多个Promise

先实现一个延迟函数

一个封装的延迟函数,然后一个装有 3,4,5 的数组,需求就是在开始执行时依次等待 3, 4, 5 秒,并在之后打印对应输出

js
function delay(time) {
  return new Promise((resolve, reject) => {
    console.log(`wait ${time}s`);
    setTimeout(() => {
      console.log('execute');
      resolve();
    }, time * 1000);
  });
}

const arr = [3, 4, 5];

期望结果

js
wait 3s // 等待3s

execute
wait 4s // 等待4s

execute
wait 5s // 等待5s

execute

方法一 reduce

前一个函数 resolve 之后执行下一个 promise

js
arr.reduce((s, v) => {
  return s.then(() => delay(v));
}, Promise.resolve());

方法二 async + 循环 + await

forEach 循环不可以,可以理解成 forEach 的回调函数本身就是异步函数,放入调用栈,结束本次遍历

js
(async function() {
  for (const v of arr) {
    await delay(v);
  }
})();

方法三 普通循环

方式 1 的本质是使用一个中间变量(上一次执行结果)来保存链式 Promise, 那我们举一反三, 换别的循环也可以实现

js
let p = Promise.resolve();
for (const i of arr) {
  p = p.then(() => delay(i));
}

方法四 递归

其中洋葱模型来自于 koa-compose 库

js
function dispatch(i, p = Promise.resolve()) {
  if (!arr[i]) return Promise.resolve();
  return p.then(() => dispatch(i + 1, delay(arr[i])));
}
dispatch(0);

方法五 for await of

for await of 和 for of 规则类似,只需要实现一个内部[Symbol.asyncIterator]方法即可 先创建出一个可异步迭代对象,然后丢到 for await of 循环即可

js
function createAsyncIterable(arr) {
  return {
    [Symbol.asyncIterator]() {
      return {
        i: 0,
        next() {
          if (this.i < arr.length) {
            return delay(arr[this.i]).then(() => ({
              value: this.i++,
              done: false,
            }));
          }

          return Promise.resolve({ done: true });
        },
      };
    },
  };
}

(async function() {
  for await (i of createAsyncIterable(arr)) {
  }
})();

方法六 generator

js
function* gen() {
  for (const v of arr) {
    yield delay(v);
  }
}

function run(gen) {
  const g = gen();

  function next(data) {
    const result = g.next(data);
    if (result.done) return result.value;
    result.value.then(function(data) {
      next(data);
    });
  }

  next();
}

run(gen);