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异步

javascript异步_JavaScript异步并在循环中等待相关推荐

  1. 深入理解JavaScript的闭包特性如何给循环中的对象添加事件

    初学者经常碰到的,即获取HTML元素集合,循环给元素添加事件.在事件响应函数中(event handler)获取对应的索引.但每次获取的都是最后一次循环的索引.原因是初学者并未理解JavaScript ...

  2. JavaScript | 声明数组并在每个循环中使用的代码

    Declare an array and we have to print its elements/items using for each loop in JavaScript. 声明一个数组,我 ...

  3. python异步asy_Python 异步编程之asyncio【转载】

    一.协程的认识 协程(Coroutine),也可以被称为微线程,是一种用户态内的上下文切换技术. 简而言之,其实就是通过一个线程实现代码块相互切换执行.例如:deffunc1():print(1) . ...

  4. python3异步task_Python异步编程4:task对象

    Task对象是指:与任务调度,和并发有关,是指帮助在事件循环中并发的向任务列表,添加多个任务.task用于并发调度协程,通过asyncio.create_task(协程对象)的方式创建Task对象,这 ...

  5. JavaScript 循环中调用异步函数的三种方法,及为什么 forEach 无法工作的分析

    JavaScript 循环中调用异步函数的三种方法,及为什么 forEach 无法工作的分析 业务分析 初版的问题 解决方案 传统的 for 循环 不使用 for 循环的解决方案 分析 forEach ...

  6. javascript迭代器_JavaScript符号,迭代器,生成器,异步/等待和异步迭代器-全部简单解释...

    javascript迭代器 by rajaraodv 通过rajaraodv JavaScript符号,迭代器,生成器,异步/等待和异步迭代器-全部简单解释 (JavaScript Symbols, ...

  7. JavaScript是如何工作的:事件循环和异步编程的崛起+ 5种使用 async/await 更好地编码方式!...

    此篇是 JavaScript是如何工作的第四篇,其它三篇可以看这里: JavaScript是如何工作的:引擎,运行时和调用堆栈的概述! JavaScript是如何工作的:深入V8引擎&编写优化 ...

  8. stm32怎么调用for循环内部的变量_循环中的异步amp;amp;循环中的闭包

    在这之前先要了解一下 for循环中let 和var的区别 var 是函数级作用域或者全局作用域,let是块级作用域 看一个例子     function foo() {       for (var  ...

  9. java for循环 等待_在forEach循环中使用异步/等待

    在forEach循环中使用async / await是否有任何问题? 我正在尝试遍历文件数组并await每个文件的内容. import fs from 'fs-promise' async funct ...

最新文章

  1. 第三次学JAVA再学不好就吃翔(part11)--基础语法之switch语句
  2. 配置tomcat支持http delete和put的方法
  3. npm run build后如何打开index.html跑起项目
  4. docker gitlab-ce
  5. 【前端】CKeditor屏蔽“浏览服务器”功能
  6. 开源软件的许可(License)
  7. 设计模式-命令模式(Command)
  8. Atitit git push 报错 remote: error: hook declined to update git push 报错 remote: error: hook declined
  9. suse linux11安装 dhcp,Suse Linux DHCP的设定过程
  10. Hbase安装教程详解
  11. android按键精灵兼容性,为什么逍遥android模拟器安装按键精灵总是出错
  12. 德国华人餐饮外卖的春天?
  13. linux开发环境 tq2440,Linux2.6.35在TQ2440的移植过程(一)
  14. 程序员的表达能力 -- 程序员是表达大师! 提高形象思维能力 表达的基本模式 结构化思维能力 成长为优秀的架构师
  15. 《灵飞经》①洪武天下 第三章 东岛三尊
  16. Python-OpenCV 的 remap函数
  17. java 过滤bom头_去除bom头.java
  18. 安利一个自动求导网站
  19. 为什么开发与测试老掐架呢
  20. Android 权限表,危险权限

热门文章

  1. Java开发热门前沿知识!成功从小公司跳槽进蚂蚁定级P6
  2. 覆盖所有面试知识点,赶紧收藏!
  3. 真香警告!2021Android高级面试题,挥泪整理面经
  4. 移动pc常用Meta标签
  5. JSP过滤器Filter配置过滤类型汇总
  6. (原创)UML要点总结
  7. 【求助】windows server 2003 64位下面网卡IP总是默认为动态分派IP
  8. AtomicStampedReference源码分析
  9. 解决FTPClient上传文件为空,显示0字节
  10. HDU - 3516 Tree Construction