javascript
javascript异步_JavaScript异步并在循环中等待
javascript異步
Basic async and await is simple. Things get a bit more complicated when you try to use await in loops.
基本的async和await很簡(jiǎn)單。 當(dāng)您嘗試在循環(huán)中使用await時(shí),事情會(huì)變得更加復(fù)雜。
In this article, I want to share some gotchas to watch out for if you intend to use await in loops.
在本文中,我想分享一些陷阱,以防您打算在循環(huán)中使用await 。
在你開(kāi)始之前 (Before you begin)
I'm going to assume you know how to use async and await. If you don't, read the previous article to familiarize yourself before continuing.
我將假設(shè)您知道如何使用async和await 。 如果不是這樣,請(qǐng)先閱讀上一篇文章以熟悉一下自己,然后再繼續(xù)。
準(zhǔn)備一個(gè)例子 (Preparing an example)
For this article, let's say you want to get the number of fruits from a fruit basket.
對(duì)于本文,假設(shè)您要從水果籃中獲取水果的數(shù)量。
const fruitBasket = {apple: 27,grape: 0,pear: 14 };You want to get the number of each fruit from the fruitBasket. To get the number of a fruit, you can use a getNumFruit function.
您想從fruitBasket中獲取每個(gè)水果的數(shù)量。 要獲取水果的數(shù)量,可以使用getNumFruit函數(shù)。
const getNumFruit = fruit => {return fruitBasket[fruit]; };const numApples = getNumFruit(“apple”); console.log(numApples); // 27Now, let's say fruitBasket lives on a remote server. Accessing it takes one second. We can mock this one-second delay with a timeout. (Please refer to the previous article if you have problems understanding the timeout code).
現(xiàn)在,假設(shè)fruitBasket位于遠(yuǎn)程服務(wù)器上。 訪問(wèn)它需要一秒鐘。 我們可以用超時(shí)模擬這一一秒鐘的延遲。 (如果您在理解超時(shí)代碼時(shí)遇到問(wèn)題,請(qǐng)參考上一篇文章 )。
const sleep = ms => {return new Promise(resolve => setTimeout(resolve, ms)); };const getNumFruit = fruit => {return sleep(1000).then(v => fruitBasket[fruit]); };getNumFruit(“apple”).then(num => console.log(num)); // 27Finally, let's say you want to use await and getNumFruit to get the number of each fruit in asynchronous function.
最后,假設(shè)您要使用await和getNumFruit來(lái)獲取異步函數(shù)中每個(gè)水果的數(shù)量。
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”); };With this, we can begin looking at await in loops.
這樣,我們就可以開(kāi)始循環(huán)查看await狀態(tài)。
在for循環(huán)中等待 (Await in a for loop)
Let's say we have an array of fruits we want to get from the fruit basket.
假設(shè)我們要從水果籃中獲取一系列水果。
const fruitsToGet = [“apple”, “grape”, “pear”];We are going to loop through this array.
我們將遍歷此數(shù)組。
const forLoop = async _ => {console.log(“Start”);for (let index = 0; index < fruitsToGet.length; index++) {// Get num of each fruit}console.log(“End”); };In the for-loop, we will use getNumFruit to get the number of each fruit. We'll also log the number into the console.
在for循環(huán)中,我們將使用getNumFruit來(lái)獲取每個(gè)水果的數(shù)量。 我們還將數(shù)字登錄到控制臺(tái)中。
Since getNumFruit returns a promise, we can await the resolved value before logging it.
由于getNumFruit返回一個(gè)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”); };When you use await, you expect JavaScript to pause execution until the awaited promise gets resolved. This means awaits in a for-loop should get executed in series.
當(dāng)使用await ,您希望JavaScript暫停執(zhí)行,直到等待的諾言得到解決為止。 這意味著await S IN for循環(huán)中,串得到執(zhí)行。
The result is what you'd expect.
結(jié)果就是您所期望的。
“Start”; “Apple: 27”; “Grape: 0”; “Pear: 14”; “End”;This behavior works with most loops (like while and for-of loops)...
此行為適用于大多數(shù)循環(huán)(例如while和for-of循環(huán))...
But it won't work with loops that require a callback. Examples of such loops that require a fallback include forEach, map, filter, and reduce. We'll look at how await affects forEach, map, and filter in the next few sections.
但它不適用于需要回調(diào)的循環(huán)。 需要回退的此類循環(huán)的示例包括forEach , map , filter和reduce 。 在接下來(lái)的幾節(jié)中,我們將了解await如何影響forEach , map和filter 。
在forEach循環(huán)中等待 (Await in a forEach loop)
We'll do the same thing as we did in the for-loop example. First, let's loop through the array of fruits.
我們將執(zhí)行與for循環(huán)示例相同的操作。 首先,讓我們遍歷一系列水果。
const forEachLoop = _ => {console.log(“Start”);fruitsToGet.forEach(fruit => {// Send a promise for each fruit});console.log(“End”); };Next, we'll try to get the number of fruits with getNumFruit. (Notice the async keyword in the callback function. We need this async keyword because await is in the callback function).
接下來(lái),我們將嘗試使用getNumFruit獲得水果的數(shù)量。 (注意回調(diào)函數(shù)中的async關(guān)鍵字。我們需要此async關(guān)鍵字,因?yàn)閍wait在回調(diào)函數(shù)中)。
const forEachLoop = _ => {console.log(“Start”);fruitsToGet.forEach(async fruit => {const numFruit = await getNumFruit(fruit);console.log(numFruit);});console.log(“End”); };You might expect the console to look like this:
您可能希望控制臺(tái)看起來(lái)像這樣:
“Start”; “27”; “0”; “14”; “End”;But the actual result is different. JavaScript proceeds to call console.log('End') before the promises in the forEach loop gets resolved.
但是實(shí)際結(jié)果是不同的。 在forEach循環(huán)中的承諾得到解決之前,JavaScript會(huì)繼續(xù)調(diào)用console.log('End') 。
The console logs in this order:
控制臺(tái)按以下順序登錄:
‘Start’ ‘End’ ‘27’ ‘0’ ‘14’JavaScript does this because forEach is not promise-aware. It cannot support async and await. You _cannot_ use await in forEach.
JavaScript之所以這樣做是因?yàn)閒orEach不支持承諾。 它不支持async和await 。 您不能在forEach使用await 。
等待地圖 (Await with map)
If you use await in a map, map will always return an array of promise. This is because asynchronous functions always return promises.
如果在map使用await ,則map始終會(huì)返回一個(gè)promise數(shù)組。 這是因?yàn)楫惒胶瘮?shù)總是返回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”;Since map always return promises (if you use await), you have to wait for the array of promises to get resolved. You can do this with await Promise.all(arrayOfPromises).
由于map總是返回promise(如果使用await ),因此必須等待promise數(shù)組得到解析。 您可以使用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”); };Here's what you get:
這是您得到的:
“Start”; “[27, 0, 14]”; “End”;You can manipulate the value you return in your promises if you wish to. The resolved values will be the values you return.
如果愿意,您可以操縱在承諾中返回的價(jià)值。 解析的值將是您返回的值。
const mapLoop = async _ => {// …const promises = fruitsToGet.map(async fruit => {const numFruit = await getNumFruit(fruit);// Adds onn fruits before returningreturn numFruit + 100;});// … };“Start”; “[127, 100, 114]”; “End”;等待過(guò)濾器 (Await with filter)
When you use filter, you want to filter an array with a specific result. Let's say you want to create an array with more than 20 fruits.
使用filter ,您要過(guò)濾具有特定結(jié)果的數(shù)組。 假設(shè)您要?jiǎng)?chuàng)建一個(gè)包含20多個(gè)水果的數(shù)組。
If you use filter normally (without await), you'll use it like this:
如果您正常使用filter (不等待),則可以這樣使用它:
// Filter if there’s no await const filterLoop = _ => {console.log(‘Start’)const moreThan20 = await fruitsToGet.filter(fruit => {const numFruit = fruitBasket[fruit]return numFruit > 20})console.log(moreThan20)console.log(‘End’) }You would expect moreThan20 to contain only apples because there are 27 apples, but there are 0 grapes and 14 pears.
您會(huì)期望moreThan20只包含一個(gè)蘋果,因?yàn)橛?7個(gè)蘋果,但是有0個(gè)葡萄和14個(gè)梨。
“Start”[“apple”]; (“End”);await in filter doesn't work the same way. In fact, it doesn't work at all. You get the unfiltered array back...
在filter中await的方式不同。 實(shí)際上,它根本不起作用。 您得到未過(guò)濾的陣列...
const filterLoop = _ => {console.log(‘Start’)const moreThan20 = await fruitsToGet.filter(async fruit => {const numFruit = getNumFruit(fruit)return numFruit > 20})console.log(moreThan20)console.log(‘End’) }“Start”[(“apple”, “grape”, “pear”)]; (“End”);Here's why it happens.
這就是它發(fā)生的原因。
When you use await in a filter callback, the callback always a promise. Since promises are always truthy, everything item in the array passes the filter. Writing await in a filter is like writing this code:
當(dāng)您在filter回調(diào)中使用await時(shí),該回調(diào)始終為promise。 由于承諾始終是真實(shí)的,因此數(shù)組中的所有項(xiàng)目都會(huì)通過(guò)過(guò)濾器。 在filter編寫(xiě)await就像編寫(xiě)以下代碼:
// Everything passes the filter… const filtered = array.filter(true);There are three steps to use await and filter properly:
可以使用三個(gè)步驟來(lái)正確使用await和filter :
1. Use map to return an array promises
1.使用map返回一個(gè)數(shù)組promises
2. await the array of promises
2. await承諾
3. filter the resolved values
3. 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”); };Start[“apple”]; End;等待減少 (Await with reduce)
For this case, let's say you want to find out the total number of fruits in the fruitBastet. Normally, you can use reduce to loop through an array and sum the number up.
對(duì)于這種情況,假設(shè)您要查找fruitBastet中的水果總數(shù)。 通常,您可以使用reduce遍歷一個(gè)數(shù)組并將其求和。
// Reduce if there’s no await 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”); };You'll get a total of 41 fruits. (27 + 0 + 14 = 41).
您總共會(huì)得到41水果。 (27 + 0 + 14 = 41)。
“Start”; “41”; “End”;When you use await with reduce, the results get extremely messy.
當(dāng)使用帶有reduce的await時(shí),結(jié)果將變得非?;靵y。
// Reduce if we await getNumFruit const reduceLoop = async _ => {console.log(“Start”);const sum = await fruitsToGet.reduce(async (sum, fruit) => {const numFruit = await getNumFruit(fruit);return sum + numFruit;}, 0);console.log(sum);console.log(“End”); };“Start”; “[object Promise]14”; “End”;What?! [object Promise]14?!
什么?! [object Promise]14嗎?
Dissecting this is interesting.
剖析這很有趣。
In the first iteration, sum is 0. numFruit is 27 (the resolved value from getNumFruit(‘a(chǎn)pple’)). 0 + 27 is 27.
在第一次迭代中, sum為0 。 numFruit為27(來(lái)自getNumFruit('apple')的解析值)。 0 + 27是27。
In the second iteration, sum is a promise. (Why? Because asynchronous functions always return promises!) numFruit is 0. A promise cannot be added to an object normally, so the JavaScript converts it to [object Promise] string. [object Promise] + 0 is [object Promise]0
在第二次迭代中, sum是一個(gè)承諾。 (為什么?因?yàn)楫惒胶瘮?shù)總是返回諾言!) numFruit為0。通常無(wú)法將諾言添加到對(duì)象,因此JavaScript將其轉(zhuǎn)換為[object Promise]字符串。 [object Promise] + 0為[object Promise]0
In the third iteration, sum is also a promise. numFruit is 14. [object Promise] + 14 is [object Promise]14.
在第三次迭代中, sum也是一個(gè)承諾。 numFruit是14 。 [object Promise] + 14是[object Promise]14 。
Mystery solved!
謎團(tuán)已揭開(kāi)!
This means, you can use await in a reduce callback, but you have to remember to await the accumulator first!
這意味著,您可以在reduce回調(diào)中使用await ,但是您必須記住要先await累加器!
const reduceLoop = async _ => {console.log(“Start”);const sum = await fruitsToGet.reduce(async (promisedSum, fruit) => {const sum = await promisedSum;const numFruit = await getNumFruit(fruit);return sum + numFruit;}, 0);console.log(sum);console.log(“End”); };“Start”; “41”; “End”;But... as you can see from the gif, it takes pretty long to await everything. This happens because reduceLoop needs to wait for the promisedSum to be completed for each iteration.
但是...從gif中可以看到, await所有內(nèi)容都需要很長(zhǎng)時(shí)間。 發(fā)生這種情況是因?yàn)閞educeLoop需要等待每次迭代的promisedSum完成。
There's a way to speed up the reduce loop. (I found out about this thanks to Tim Oxley. If you await getNumFruits() first before await promisedSum, the reduceLoop takes only one second to complete:
有一種方法可以加快reduce循環(huán)。 (我感謝Tim Oxley對(duì)此進(jìn)行了了解。如果在await getNumFruits(之前先await promisedSum await getNumFruits( ),則reduceLoop僅需一秒鐘即可完成:
const reduceLoop = async _ => {console.log(“Start”);const sum = await fruitsToGet.reduce(async (promisedSum, fruit) => {// Heavy-lifting comes first.// This triggers all three getNumFruit promises before waiting for the next iteration of the loop.const numFruit = await getNumFruit(fruit);const sum = await promisedSum;return sum + numFruit;}, 0);console.log(sum);console.log(“End”); };This works because reduce can fire all three getNumFruit promises before waiting for the next iteration of the loop. However, this method is slightly confusing since you have to be careful of the order you await things.
之所以getNumFruit是因?yàn)閞educe可以在等待循環(huán)的下一次迭代之前觸發(fā)所有三個(gè)getNumFruit承諾。 但是,此方法有些混亂,因?yàn)槟仨氉⒁鈇wait的順序。
The simplest (and most efficient way) to use await in reduce is to:
在reduce中使用await的最簡(jiǎn)單(也是最有效的方式)是:
1. Use map to return an array promises
1.使用map返回一個(gè)數(shù)組promises
2. await the array of promises
2. await承諾
3. reduce the resolved values
3. 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”); };This version is simple to read and understand, and takes one second to calculate the total number of fruits.
此版本易于閱讀和理解,并且花費(fèi)一秒鐘來(lái)計(jì)算水果總數(shù)。
重要要點(diǎn) (Key Takeaways)
1. If you want to execute await calls in series, use a for-loop (or any loop without a callback).
1.如果要連續(xù)執(zhí)行await調(diào)用,請(qǐng)使用for-loop (或任何沒(méi)有回調(diào)的循環(huán))。
2. Don't ever use await with forEach. Use a for-loop (or any loop without a callback) instead.
2.永遠(yuǎn)不要在forEach使用await 。 請(qǐng)使用for-loop (或任何沒(méi)有回調(diào)的循環(huán))。
3. Don't await inside filter and reduce. Always await an array of promises with map, then filter or reduce accordingly.
3.不要在filter內(nèi)部await并reduce 。 始終await帶有map的promise數(shù)組,然后相應(yīng)地filter或reduce 。
This article was originally posted on my blog. Sign up for my newsletter if you want more articles to help you become a better frontend developer.
本文最初發(fā)布在我的博客上 。 如果您想獲得更多文章來(lái)幫助您成為更好的前端開(kāi)發(fā)人員,請(qǐng)注冊(cè)我的時(shí)事通訊 。
翻譯自: https://www.freecodecamp.org/news/javascript-async-and-await-in-loops-30ecc5fb3939/
javascript異步
總結(jié)
以上是生活随笔為你收集整理的javascript异步_JavaScript异步并在循环中等待的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: 孕妇梦到地瓜是什么意思
- 下一篇: 做梦梦到自己喜欢的人是什么意思