async 与 await 的使用方式相对简单。 但当你尝试在循环中使用await时,事情就会变得复杂一些。
在本文中,分享一些在如果循环中使用await值得注意的问题。
准备一个例子对于这篇文章,假设你想从水果篮中获取水果的数量。
const fruitbasket = { apple: 27, grape: 0, pear: 14};
你想从fruitbasket获得每个水果的数量。 要获取水果的数量,可以使用getnumfruit函数。
const getnumfruit = fruit => { return fruitbasket[fruit];};const numapples = getnumfruit('apple');console.log(numapples); //27
现在,假设fruitbasket是从服务器上获取,这里我们使用 settimeout 来模拟。
const sleep = ms => { return new promise(resolve => settimeout(resolve, ms))};const getnumfruie = fruit => { return sleep(1000).then(v => fruitbasket[fruit]);};getnumfruit(apple).then(num => console.log(num)); // 27
最后,假设你想使用await和getnumfruit来获取异步函数中每个水果的数量。
const control = async _ => { console.log('start') const numapples = await getnumfruit('apple'); console.log(numapples); const numgrapes = await getnumfruit('grape'); console.log(numgrapes); const numpears = await getnumfruit('pear'); console.log(numpears); console.log('end')}
在 for 循环中使用 await首先定义一个存放水果的数组:
const fruitstoget = [“apple”, “grape”, “pear”];
循环遍历这个数组:
const forloop = async _ => { console.log('start'); for (let index = 0; index < fruitstoget.length; index++) { // 得到每个水果的数量 } console.log('end')}
在for循环中,过上使用getnumfruit来获取每个水果的数量,并将数量打印到控制台。
由于getnumfruit返回一个promise,我们使用 await 来等待结果的返回并打印它。
const forloop = async _ => { console.log('start'); for (let index = 0; index < fruitstoget.length; index ++) { const fruit = fruitstoget[index]; const numfruit = await getnumfruit(fruit); console.log(numfruit); } console.log('end')}
当使用await时,希望javascript暂停执行,直到等待 promise 返回处理结果。这意味着for循环中的await 应该按顺序执行。
结果正如你所预料的那样。
“start”;“apple: 27”;“grape: 0”;“pear: 14”;“end”;
这种行为适用于大多数循环(比如while和for-of循环)…
但是它不能处理需要回调的循环,如foreach、map、filter和reduce。在接下来的几节中,我们将研究await 如何影响foreach、map和filter。
在 foreach 循环中使用 await首先,使用 foreach 对数组进行遍历。
const foreach = _ => { console.log('start'); fruitstoget.foreach(fruit => { //... }) console.log('end')}
接下来,我们将尝试使用getnumfruit获取水果数量。 (注意回调函数中的async关键字。我们需要这个async关键字,因为await在回调函数中)。
const foreachloop = _ => { console.log('start'); fruitstoget.foreach(async fruit => { const numfruit = await getnumfruit(fruit); console.log(numfruit) }); console.log('end')}
我期望控制台打印以下内容:
“start”;“27”;“0”;“14”;“end”;
但实际结果是不同的。在foreach循环中等待返回结果之前,javascrip先执行了 console.log('end')。
实际控制台打印如下:
‘start’‘end’‘27’‘0’‘14’
javascript 中的 foreach不支持 promise 感知,也支持 async 和await,所以不能在 foreach 使用 await 。
在 map 中使用 await如果在map中使用await, map 始终返回promise数组,这是因为异步函数总是返回promise。
const maploop = async _ => { console.log('start') const numfruits = await fruitstoget.map(async fruit => { const numfruit = await getnumfruit(fruit); return numfruit; }) console.log(numfruits); console.log('end')} “start”;“[promise, promise, promise]”;“end”;
如果你在 map 中使用 await,map 总是返回promises,你必须等待promises 数组得到处理。 或者通过await promise.all(arrayofpromises)来完成此操作。
const maploop = async _ => { console.log('start'); const promises = fruitstoget.map(async fruit => { const numfruit = await getnumfruit(fruit); return numfruit; }); const numfruits = await promise.all(promises); console.log(numfruits); console.log('end')}
运行结果如下:
如果你愿意,可以在promise 中处理返回值,解析后的将是返回的值。
const maploop = _ => { // ... const promises = fruitstoget.map(async fruit => { const numfruit = await getnumfruit(fruit); return numfruit + 100 }) // ...} “start”;“[127, 100, 114]”;“end”;
在 filter 循环中使用 await当你使用filter时,希望筛选具有特定结果的数组。假设过滤数量大于20的数组。
如果你正常使用filter (没有 await),如下:
const filterloop = _ => { console.log('start') const morethan20 = fruitstoget.filter(async fruit => { const numfruit = await fruitbasket[fruit] return numfruit > 20 }) console.log(morethan20) console.log('end')}
运行结果
start[apple]end
filter 中的await不会以相同的方式工作。 事实上,它根本不起作用。
const filterloop = async _ => { console.log('start') const morethan20 = await fruitstoget.filter(async fruit => { const numfruit = fruitbasket[fruit] return numfruit > 20 }) console.log(morethan20) console.log('end')}// 打印结果start[apple, grape, pear]end
为什么会发生这种情况?
当在filter 回调中使用await时,回调总是一个promise。由于promise 总是真的,数组中的所有项都通过filter 。在filter 使用 await类以下这段代码
const filtered = array.filter(true);
在filter使用 await 正确的三个步骤
使用map返回一个promise 数组使用 await 等待处理结果使用 filter 对返回的结果进行处理const filterloop = async _ => { console.log('start'); const promises = await fruitstoget.map(fruit => getnumfruit(fruit)); const numfruits = await promise.all(promises); const morethan20 = fruitstoget.filter((fruit, index) => { const numfruit = numfruits[index]; return numfruit > 20; }) console.log(morethan20); console.log('end')}
在 reduce 循环中使用 await如果想要计算 fruitbastet中的水果总数。 通常,你可以使用reduce循环遍历数组并将数字相加。
const reduceloop = _ => { console.log('start'); const sum = fruitstoget.reduce((sum, fruit) => { const numfruit = fruitbasket[fruit]; return sum + numfruit; }, 0) console.log(sum) console.log('end')}
运行结果:
当你在 reduce 中使用await时,结果会变得非常混乱。
const reduceloop = async _ => { console.log('start'); const sum = await fruitstoget.reduce(async (sum, fruit) => { const numfruit = await fruitbasket[fruit]; return sum + numfruit; }, 0) console.log(sum) console.log('end')}
[object promise]14 是什么 鬼??
剖析这一点很有趣。
在第一次遍历中,sum为0。numfruit是27(通过getnumfruit(apple)的得到的值),0 + 27 = 27。在第二次遍历中,sum是一个promise。 (为什么?因为异步函数总是返回promises!)numfruit是0.promise 无法正常添加到对象,因此javascript将其转换为[object promise]字符串。 [object promise] + 0 是object promise] 0。在第三次遍历中,sum 也是一个promise。 numfruit是14. [object promise] + 14是[object promise] 14。解开谜团!
这意味着,你可以在reduce回调中使用await,但是你必须记住先等待累加器!
const reduceloop = async _ => { console.log('start'); const sum = await fruitstoget.reduce(async (promisedsum, fruit) => { const sum = await promisedsum; const numfruit = await fruitbasket[fruit]; return sum + numfruit; }, 0) console.log(sum) console.log('end')}
但是从上图中看到的那样,await 操作都需要很长时间。 发生这种情况是因为reduceloop需要等待每次遍历完成promisedsum。
有一种方法可以加速reduce循环,如果你在等待promisedsum之前先等待getnumfruits(),那么reduceloop只需要一秒钟即可完成:
const reduceloop = async _ => { console.log('start'); const sum = await fruitstoget.reduce(async (promisedsum, fruit) => { const numfruit = await fruitbasket[fruit]; const sum = await promisedsum; return sum + numfruit; }, 0) console.log(sum) console.log('end')}
这是因为reduce可以在等待循环的下一个迭代之前触发所有三个getnumfruit promise。然而,这个方法有点令人困惑,因为你必须注意等待的顺序。
在reduce中使用wait最简单(也是最有效)的方法是
使用map返回一个promise 数组使用 await 等待处理结果使用 reduce 对返回的结果进行处理const reduceloop = async _ => {
console.log('start');
const promises = fruitstoget.map(getnumfruit);
const numfruits = await promise.all(promises);
const sum = numfruits.reduce((sum, fruit) => sum + fruit);
console.log(sum)
console.log('end')
}
这个版本易于阅读和理解,需要一秒钟来计算水果总数。
从上面看出来什么如果你想连续执行await调用,请使用for循环(或任何没有回调的循环)。永远不要和foreach一起使用await,而是使用for循环(或任何没有回调的循环)。不要在 filter 和 reduce 中使用 await,如果需要,先用 map 进一步骤处理,然后在使用 filter 和 reduce 进行处理。原文地址:https://medium.com/free-code-camp/javascript-async-and-await-in-loops-30ecc5fb3939
更多编程相关知识,请访问:编程学习网站!!
以上就是javascript循环中如何使用 async/await?需要注意些什么?的详细内容。
