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.mzph.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//…