问题描述

现有 n 个异步任务,这 n 个异步任务是依次执行且下一个异步任务依赖上一个异步任务的结果作参数,问如何实现。

解法1:for 循环 + await

简单的 for 循环是依次进行循环的,不像 Array.forEach,Array.map 方法是并发执行的,利用这一特点加 async / await 很容易写出下面这样的代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
(async () => {
const sleep = delay => {
return new Promise((resolve, reject) => {
setTimeout(_ => resolve(), delay)
})
}

const task = (i) => {
return new Promise(async (resolve, reject) => {
await sleep(500)
console.log(`now is ${i}`)
++i
resolve(i)
})
}

let param = 0
for (let i = 0; i < 4; i++) {
param = await task(param)
}
})()

输出:

1
2
3
4
now is 0
now is 1
now is 2
now is 3

效果虽然做到了,但是看到 param 这个局部变量就很不爽,请看解法2。

解法2:Array.prototype.reduce

关于 Array.prototype.reduce 方法相信大部分小伙伴初见时都是用来数组求和。不了解的小伙伴可以点击链接了解下 reduce

reduce初始值积累值,以及当前值的概念。其中 积累值可以看作是前一个值,通过返回积累值又可以看作是 下一个值(可能说的比较绕,可以参照 Redux 的 中间件执行顺序 的源码,也是用的 reduce)。使用reduce来解决问题的代码为:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
const sleep = delay => {
return new Promise((resolve, reject) => {
setTimeout(_ => resolve(), delay)
})
}

const task = (i) => {
return new Promise(async (resolve, reject) => {
await sleep(500)
console.log(`now is ${i}`)
++i
resolve(i)
})
}

[task, task, task, task].reduce(async (prev, task) => {
const res = await prev
return task(res)
}, 0)

输出:

1
2
3
4
now is 0
now is 1
now is 2
now is 3

可以这样理解 prevtask

  • prev:前一个 异步任务(promise)
  • task:当前的异步任务

当前的异步任务需要上一个异步任务的结果作参数,故很显然要 await prev