在循环中使用 async/await
掘金安东尼 2021-03-19
JS
我们经常会遇到这样的需求,在循环中使用异步请求 async/await。
本篇总结了 5 种使用方法:
- 打勾的方法 ✔ :表示在循环中每个异步请求是按照次序来执行的,我们简称为 “串行”
- 打叉的方法❌:代表在循环中执行了所有异步请求,不保证次序,我们简称为 “并行”
按需所取,点赞👍收藏📕
# forEach ❌
遍历我们常用 forEach,用 forEach 可以吗?
首先要明确的是,本质上 forEach 就是一个 for 循环的包装。
Array.prototype.forEach = function (callback) {
for (let index = 0; index < this.length; index++) {
callback(this[index], index, this)
}
}
1
2
3
4
5
2
3
4
5
在回调函数内部调用 await 需要这个回调函数本身也是 async 函数,所以循环中的代码应这样写:
async function someFunction(items) {
items.forEach( async(i) => {
const res = await someAPICall(i);
console.log('--->', res);
});
}
function someAPICall(param) {
return new Promise((resolve, reject)=>{
setTimeout(()=>{
resolve("Resolved" + param)
},param);
})
}
someFunction(['3000','8000','1000','4000']);
1
2
3
4
5
6
7
8
9
10
11
12
13
14
2
3
4
5
6
7
8
9
10
11
12
13
14
在控制台执行,如图:
我们可以看到 forEach 并没有按照我们的期望进行输出。forEach 只是按顺序并行执行了,而不是串行。而用 for...of 却符合我们的要求。
# for...of... ✔
async function printFiles () {
let fileNames = ['picard', 'kirk', 'geordy', 'ryker', 'worf'];
for (const file of fileNames) {
const contents = await fs.readFile(file, 'utf8');
console.log(contents);
}
}
1
2
3
4
5
6
7
2
3
4
5
6
7
控制台执行如下图:
# reduce ✔
其实有了解过【循环】+【异步】的童鞋肯定知道 reduce。
function testPromise(time) {
return new Promise((resolve, reject) => {
setTimeout(() => {
console.log(`Processing ${time}`);
resolve(time);
}, time);
});
}
let result = [3000,2000,1000, 4000].reduce( (accumulatorPromise, nextID) => {
return accumulatorPromise.then(() => {
return testPromise(nextID);
});
}, Promise.resolve());
result.then(e => {
console.log("All Promises Resolved !!✨")
});
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
我们可以使用 reduce 函数来遍历数组并按顺序 resolve promise。
很清晰!
# generator ✔
其实用 async generator 也是可以的。
async function* readFiles(files) {
for(const file of files) {
yield await readFile(file);
}
};
1
2
3
4
5
2
3
4
5
针对上例,代码如下:
async function* generateSequence(items) {
for (const i of items) {
await new Promise(resolve => setTimeout(resolve, i));
yield i;
}
}
(async () => {
let generator = generateSequence(['3000','8000','1000','4000']);
for await (let value of generator) {
console.log(value);
}
})();
1
2
3
4
5
6
7
8
9
10
11
12
13
2
3
4
5
6
7
8
9
10
11
12
13
# Promise.all() ❌
如果你不用考虑异步请求的执行顺序,你可以选择 Promise.all(),即 Promise.all() 可以达到并行 而非串行的目的。
async function printFiles () {
let fileNames = ['picard', 'kirk', 'geordy', 'ryker', 'worf'];
await Promise.all(fileNames.map(async (file) => {
const contents = await fs.readFile(file, 'utf8');
console.log(contents);
}));
}
1
2
3
4
5
6
7
2
3
4
5
6
7
针对上例,代码如下:
async function promiseAll(arr) {
await Promise.all(arr.map(async (i) => {
await sleep(i)
console.log('--->', i);
}))
}
function sleep(i) {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve()
}, i)
})
}
promiseAll(['3000','8000','1000','4000'])
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16