我们经常会遇到这样的需求,在循环中使用异步请求,而 ES6 的 async/await 是我们让异步编程更简单的利剑🗡。
本篇总结了 5 种在循环中使用 async/await 的方法(代码干货都能在浏览器控制台自测):
- 打勾的方法 ✔:表示在循环中每个异步请求是按照次序来执行的,我们简称为 “串行”
- 打叉的方法 ❌ :表示只借助循环执行所有异步请求,不保证次序,我们简称为 “并行”
按需所取,点赞👍收藏📕
forEach ❌
首先,想到遍历,我们常用 forEach,用 forEach 可以吗?来试试~
首先要明确的是,本质上 forEach 就是一个 for 循环的包装。
Array.prototype.forEach = function (callback) { for (let index = 0; index < this.length; index++) { callback(this[index], index, this) } }
在回调函数内部调用 await 需要这个回调函数本身也是 async 函数,所以在【循环+ async/await】中的代码应这样写:
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']);
在控制台执行,如图:
我们可以看到 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); } }
针对本文例,代码如下:
async function someFunction(items) { for (const i of items){ 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']);
控制台执行如下图:
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 !!✨") });
我们可以使用 reduce 函数来遍历数组并按顺序 resolve promise。
很清晰!自行控制台体验。
generator ✔
其实用 async generator 也是可以的。
async function* readFiles(files) { for(const file of files) { yield await readFile(file); } };
针对本文例,代码如下:
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); } })();
自行控制台体验。
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); })); }
针对本文例,代码如下:
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'])
自行控制台体验。
好啦~
本次总结就到这!主要是代码部分,自己敲一敲,或者跑一跑、改一改,感受感受异同。
正所谓“代码用时方恨少呀“!😄按需所取,点赞👍收藏📕。
关注我的公众号【掘金安东尼】,持续输出中......