javascript異步_JavaScript異步并在循環中等待

javascript異步

Basic async and await is simple. Things get a bit more complicated when you try to use await in loops.

基本的asyncawait很簡單。 當您嘗試在循環中使用await時,事情會變得更加復雜。

In this article, I want to share some gotchas to watch out for if you intend to use await in loops.

在本文中,我想分享一些陷阱,以防您打算在循環中使用await

在你開始之前 (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.

我將假設您知道如何使用asyncawait 。 如果不是這樣,請先閱讀上一篇文章以熟悉一下自己,然后再繼續。

準備一個例子 (Preparing an example)

For this article, let's say you want to get the number of fruits from a fruit basket.

對于本文,假設您要從水果籃中獲取水果的數量。

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中獲取每個水果的數量。 要獲取水果的數量,可以使用getNumFruit函數。

const getNumFruit = fruit => {return fruitBasket[fruit];
};const numApples = getNumFruit(“apple”);
console.log(numApples); // 27

Now, 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).

現在,假設fruitBasket位于遠程服務器上。 訪問它需要一秒鐘。 我們可以用超時模擬這一一秒鐘的延遲。 (如果您在理解超時代碼時遇到問題,請參考上一篇文章 )。

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)); // 27

Finally, let's say you want to use await and getNumFruit to get the number of each fruit in asynchronous function.

最后,假設您要使用awaitgetNumFruit來獲取異步函數中每個水果的數量。

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.

這樣,我們就可以開始循環查看await狀態。

在for循環中等待 (Await in a for loop)

Let's say we have an array of fruits we want to get from the fruit basket.

假設我們要從水果籃中獲取一系列水果。

const fruitsToGet = [“apple”, “grape”, “pear”];

We are going to loop through this array.

我們將遍歷此數組。

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循環中,我們將使用getNumFruit來獲取每個水果的數量。 我們還將數字登錄到控制臺中。

Since getNumFruit returns a promise, we can await the resolved value before logging it.

由于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”);
};

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.

當使用await ,您希望JavaScript暫停執行,直到等待的諾言得到解決為止。 這意味著await S IN for循環中,串得到執行。

The result is what you'd expect.

結果就是您所期望的。

“Start”;
“Apple: 27”;
“Grape: 0”;
“Pear: 14”;
“End”;

This behavior works with most loops (like while and for-of loops)...

此行為適用于大多數循環(例如whilefor-of循環)...

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.

但它不適用于需要回調的循環。 需要回退的此類循環的示例包括forEachmapfilterreduce 。 在接下來的幾節中,我們將了解await如何影響forEachmapfilter

在forEach循環中等待 (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.

我們將執行與for循環示例相同的操作。 首先,讓我們遍歷一系列水果。

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).

接下來,我們將嘗試使用getNumFruit獲得水果的數量。 (注意回調函數中的async關鍵字。我們需要此async關鍵字,因為await在回調函數中)。

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:

您可能希望控制臺看起來像這樣:

“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.

但是實際結果是不同的。 在forEach循環中的承諾得到解決之前,JavaScript會繼續調用console.log('End')

The console logs in this order:

控制臺按以下順序登錄:

‘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之所以這樣做是因為forEach不支持承諾。 它不支持asyncawait 。 您不能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始終會返回一個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”;

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數組得到解析。 您可以使用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.

如果愿意,您可以操縱在承諾中返回的價值。 解析的值將是您返回的值。

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”;

等待過濾器 (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 ,您要過濾具有特定結果的數組。 假設您要創建一個包含20多個水果的數組。

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.

您會期望moreThan20只包含一個蘋果,因為有27個蘋果,但是有0個葡萄和14個梨。

“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...

filterawait的方式不同。 實際上,它根本不起作用。 您得到未過濾的陣列...

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.

這就是它發生的原因。

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:

當您在filter回調中使用await時,該回調始終為promise。 由于承諾始終是真實的,因此數組中的所有項目都會通過過濾器。 在filter編寫await就像編寫以下代碼:

// Everything passes the filter…
const filtered = array.filter(true);

There are three steps to use await and filter properly:

可以使用三個步驟來正確使用awaitfilter

1. Use map to return an array promises

1.使用map返回一個數組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.

對于這種情況,假設您要查找fruitBastet中的水果總數。 通常,您可以使用reduce遍歷一個數組并將其求和。

// 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).

您總共會得到41水果。 (27 + 0 + 14 = 41)。

“Start”;
“41”;
“End”;

When you use await with reduce, the results get extremely messy.

當使用帶有reduce的await時,結果將變得非常混亂。

// 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(‘apple’)). 0 + 27 is 27.

    在第一次迭代中, sum0numFruit為27(來自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是一個承諾。 (為什么?因為異步函數總是返回諾言!) numFruit為0。通常無法將諾言添加到對象,因此JavaScript將其轉換為[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也是一個承諾。 numFruit14[object Promise] + 14[object Promise]14

Mystery solved!

謎團已揭開!

This means, you can use await in a reduce callback, but you have to remember to await the accumulator first!

這意味著,您可以在reduce回調中使用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所有內容都需要很長時間。 發生這種情況是因為reduceLoop需要等待每次迭代的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循環。 (我感謝Tim Oxley對此進行了了解。如果在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是因為reduce可以在等待循環的下一次迭代之前觸發所有三個getNumFruit承諾。 但是,此方法有些混亂,因為您必須注意await的順序。

The simplest (and most efficient way) to use await in reduce is to:

在reduce中使用await的最簡單(也是最有效的方式)是:

1. Use map to return an array promises

1.使用map返回一個數組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.

此版本易于閱讀和理解,并且花費一秒鐘來計算水果總數。

重要要點 (Key Takeaways)

1. If you want to execute await calls in series, use a for-loop (or any loop without a callback).

1.如果要連續執行await調用,請使用for-loop (或任何沒有回調的循環)。

2. Don't ever use await with forEach. Use a for-loop (or any loop without a callback) instead.

2.永遠不要在forEach使用await 。 請使用for-loop (或任何沒有回調的循環)。

3. Don't await inside filter and reduce. Always await an array of promises with map, then filter or reduce accordingly.

3.不要在filter內部awaitreduce 。 始終await帶有map的promise數組,然后相應地filterreduce

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.

本文最初發布在我的博客上 如果您想獲得更多文章來幫助您成為更好的前端開發人員,請注冊我的時事通訊 。

翻譯自: https://www.freecodecamp.org/news/javascript-async-and-await-in-loops-30ecc5fb3939/

javascript異步

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/391638.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/391638.shtml
英文地址,請注明出處:http://en.pswp.cn/news/391638.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

白盒測試目錄導航

白盒測試目錄導航&#xff08;更新中&#xff09; 2017-12-29 [1] 白盒測試&#xff1a;為什么要做白盒測試 [2] 白盒測試&#xff1a;理論基礎 [3] 白盒測試實戰&#xff08;上&#xff09; [4] 白盒測試實戰&#xff08;中&#xff09; [5] 白盒測試實戰&#xff08;下&#…

hdf5文件和csv的區別_使用HDF5文件并創建CSV文件

hdf5文件和csv的區別In my last article, I discussed the steps to download NASA data from GES DISC. The data files downloaded are in the HDF5 format. HDF5 is a file format, a technology, that enables the management of very large data collections. Thus, it is…

CSS仿藝龍首頁鼠標移入圖片放大

CSS仿藝龍首頁鼠標移入圖片放大&#xff0c;效果似乎沒有js好。。。。。。 <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><title>圖片放大</title><style>*{padding:0;margin:0;}body{padding-…

leetcode 224. 基本計算器(棧)

給你一個字符串表達式 s &#xff0c;請你實現一個基本計算器來計算并返回它的值。 示例 1&#xff1a; 輸入&#xff1a;s “1 1” 輸出&#xff1a;2 示例 2&#xff1a; 輸入&#xff1a;s " 2-1 2 " 輸出&#xff1a;3 示例 3&#xff1a; 輸入&#xff…

機械制圖國家標準的繪圖模板_如何使用p5js構建繪圖應用

機械制圖國家標準的繪圖模板The theme for week #5 of the Weekly Coding Challenge is:每周編碼挑戰第5周的主題是&#xff1a; 創建繪圖應用程序 (Creating a Drawing Application) This is the first application that we are building in the #weeklyCodingChallenge prog…

機器學習常用模型:決策樹_fairmodels:讓我們與有偏見的機器學習模型作斗爭

機器學習常用模型:決策樹TL; DR (TL;DR) The R Package fairmodels facilitates bias detection through model visualizations. It implements a few mitigation strategies that could reduce bias. It enables easy to use checks for fairness metrics and comparison betw…

高德地圖如何將比例尺放大到10米?

2019獨角獸企業重金招聘Python工程師標準>>> var map new AMap.Map(container, {resizeEnable: true,expandZoomRange:true,zoom:20,zooms:[3,20],center: [116.397428, 39.90923] }); alert(map.getZoom());http://lbs.amap.com/faq/web/javascript-api/expand-zo…

Android 手把手帶你玩轉自己定義相機

本文已授權微信公眾號《鴻洋》原創首發&#xff0c;轉載請務必注明出處。概述 相機差點兒是每一個APP都要用到的功能&#xff0c;萬一老板讓你定制相機方不方&#xff1f;反正我是有點方。關于相機的兩天奮斗總結免費送給你。Intent intent new Intent(); intent.setAction(M…

如何在JavaScript中克隆數組

JavaScript has many ways to do anything. I’ve written on 10 Ways to Write pipe/compose in JavaScript, and now we’re doing arrays.JavaScript有許多方法可以執行任何操作。 我已經寫了10種用JavaScript編寫管道/組合的方法 &#xff0c;現在我們正在做數組。 1.傳播…

leetcode 227. 基本計算器 II(棧)

給你一個字符串表達式 s &#xff0c;請你實現一個基本計算器來計算并返回它的值。 整數除法僅保留整數部分。 示例 1&#xff1a; 輸入&#xff1a;s “32*2” 輸出&#xff1a;7 解題思路 利用兩個棧&#xff0c;一個記錄操作數&#xff0c;一個記錄操作符&#xff0c;…

100米隊伍,從隊伍后到前_我們的隊伍

100米隊伍,從隊伍后到前The last twelve months have brought us a presidential impeachment trial, the coronavirus pandemic, sweeping racial justice protests triggered by the death of George Floyd, and a critical presidential election. News coverage of these e…

idea使用 git 撤銷commit

2019獨角獸企業重金招聘Python工程師標準>>> 填寫commit的id 就可以取消這一次的commit 轉載于:https://my.oschina.net/u/3559695/blog/1596669

ES6標準入門(第二版)pdf

下載地址&#xff1a;網盤下載 內容簡介 ES6&#xff08;又名 ES2105&#xff09;是 JavaScript 語言的新標準&#xff0c;2015 年 6 月正式發布后&#xff0c;得到了迅速推廣&#xff0c;是目前業界超級活躍的計算機語言。《ES6標準入門&#xff08;第2版&#xff09;》…

hexo博客添加暗色模式_我如何向網站添加暗模式

hexo博客添加暗色模式同一個網站&#xff0c;兩種不同的配色方案 (Same website, two different color schemes) Last year I made it a point to redesign my website from scratch. I wanted something simple and minimalist looking that clearly stated what this was — …

leetcode 331. 驗證二叉樹的前序序列化

序列化二叉樹的一種方法是使用前序遍歷。當我們遇到一個非空節點時&#xff0c;我們可以記錄下這個節點的值。如果它是一個空節點&#xff0c;我們可以使用一個標記值記錄&#xff0c;例如 #。_9_/ \3 2/ \ / \4 1 # 6 / \ / \ / \ # # # # # # 例如&#xff0…

mongodb數據可視化_使用MongoDB實時可視化開放數據

mongodb數據可視化Using Python to connect to Taiwan Government PM2.5 open data API, and schedule to update data in real time to MongoDB — Part 2使用Python連接到臺灣政府PM2.5開放數據API&#xff0c;并計劃將數據實時更新到MongoDB —第2部分 目標 (Goal) This ti…

4.kafka的安裝部署

為了安裝過程對一些參數的理解&#xff0c;我先在這里提一下kafka一些重點概念,topic,broker,producer,consumer,message,partition,依賴于zookeeper, kafka是一種消息隊列,他的服務端是由若干個broker組成的&#xff0c;broker會向zookeeper&#xff0c;producer生成者對應一個…

javascript初學者_針對JavaScript初學者的調試技巧和竅門

javascript初學者by Priyanka Garg由Priyanka Garg My intended audience for this tutorial is beginner programmers. You’ll learn about frustration-free debugging with chrome dev tools.本教程的目標讀者是初學者。 您將學習使用chrome開發工具進行無挫折的調試。 D…

leetcode 705. 設計哈希集合

不使用任何內建的哈希表庫設計一個哈希集合&#xff08;HashSet&#xff09;。 實現 MyHashSet 類&#xff1a; void add(key) 向哈希集合中插入值 key 。 bool contains(key) 返回哈希集合中是否存在這個值 key 。 void remove(key) 將給定值 key 從哈希集合中刪除。如果哈希…

ecshop 前臺個人中心修改側邊欄 和 側邊欄顯示不全 或 導航現實不全

怎么給個人中心側邊欄加項或者減項 在模板文件default/user_menu.lbi 文件里添加或者修改,一般看到頁面都會知道怎么加,怎么刪,這里就不啰嗦了 添加一個欄目以后,這個地址跳的頁面怎么寫 這是最基本的一個包括左側個人信息,頭部導航欄 <!DOCTYPE html PUBLIC "-//W3C//…