重学 JS:为啥 await 不能用在 forEach 中详解


Posted in Javascript onApril 15, 2019

这是重学 JS 系列的第三篇文章,写这个系列的初衷也是为了夯实自己的 JS 基础或者了解一些之前不知道的东西。既然是重学,肯定不会从零开始介绍一个知识点,如有遇到不会的内容请自行查找资料。

不知道你有没有写过类似的代码,反正以前我是写过

function test() {
 let arr = [3, 2, 1]
 arr.forEach(async item => {
  const res = await fetch(item)
  console.log(res)
 })
 console.log('end')
}

function fetch(x) {
 return new Promise((resolve, reject) => {
  setTimeout(() => {
   resolve(x)
  }, 500 * x)
 })
}

test()

我当时期望的打印顺序是

3
2
1
end

 结果现实与我开了个玩笑,打印顺序居然是

end
1
2
3

为什么?

其实原因很简单,那就是 forEach 只支持同步代码。

我们可以参考下 Polyfill 版本的 forEach,简化以后类似就是这样的伪代码

while (index < arr.length) {
  // 也就是我们传入的回调函数
  callback(item, index)
}

从上述代码中我们可以发现,forEach 只是简单的执行了下回调函数而已,并不会去处理异步的情况。并且你在 callback 中即使使用 break 也并不能结束遍历。

怎么解决?

一般来说解决的办法有两种。

第一种是使用 Promise.all 的方式

async function test() {
 let arr = [3, 2, 1]
 await Promise.all(
  arr.map(async item => {
   const res = await fetch(item)
   console.log(res)
  })
 )
 console.log('end')
}

这样可以生效的原因是 async 函数肯定会返回一个 Promise 对象,调用 map 以后返回值就是一个存放了 Promise 的数组了,这样我们把数组传入 Promise.all 中就可以解决问题了。但是这种方式其实并不能达成我们要的效果,如果你希望内部的 fetch 是顺序完成的,可以选择第二种方式。

第一种方法是使用 for...of

async function test() {
 let arr = [3, 2, 1]
 for (const item of arr) {
  const res = await fetch(item)
  console.log(res)
 }
 console.log('end')
}

这种方式相比 Promise.all 要简洁的多,并且也可以实现开头我想要的输出顺序。

但是这时候你是否又多了一个疑问?为啥 for...of 内部就能让 await 生效呢。

因为 for...of 内部处理的机制和 forEach 不同,forEach 是直接调用回调函数,for...of 是通过迭代器的方式去遍历。

async function test() {
 let arr = [3, 2, 1]
 const iterator = arr[Symbol.iterator]()
 let res = iterator.next()
 while (!res.done) {
  const value = res.value
  const res1 = await fetch(value)
  console.log(res1)
  res = iterator.next()
 }
 console.log('end')
}

最后
以上就是本篇文章的全部内容了,如果你还有什么疑问欢迎在评论区与我互动。

我所有的系列文章都会在我的 Github 中最先更新,有兴趣的可以关注下。今年主要会着重写以下三个专栏

重学 JS
React 进阶
重写组件

以上所述是小编给大家介绍的为啥await 不能用在 forEach 中详解整合,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对三水点靠木网站的支持!

Javascript 相关文章推荐
JQuery 自定义CircleAnimation,Animate方法学习笔记
Jul 10 Javascript
jQuery Ajax提交表单查询获得数据实例代码
Sep 19 Javascript
多种方法实现JS动态添加事件
Nov 01 Javascript
jquery1.9 下检测浏览器类型和版本的方法
Dec 26 Javascript
javascript实现支持移动设备画廊
Aug 24 Javascript
给angular加上动画效遇到的问题总结
Feb 17 Javascript
Vue.js常用指令汇总(v-if、v-for等)
Nov 03 Javascript
bootstrap weebox 支持ajax的模态弹出框
Feb 23 Javascript
微信小程序 下拉菜单的实现
Apr 06 Javascript
微信小程序wx.getImageInfo()如何获取图片信息
Jan 26 Javascript
原生JS实现的跳一跳小游戏完整实例
Jan 27 Javascript
Vue+ElementUI 中级联选择器Bug问题的解决
Jul 31 Javascript
你不知道的Vue技巧之--开发一个可以通过方法调用的组件(推荐)
Apr 15 #Javascript
详解JavaScript中的强制类型转换
Apr 15 #Javascript
一个小时快速搭建微信小程序的方法步骤
Apr 15 #Javascript
详解从0开始搭建微信小程序(前后端)的全过程
Apr 15 #Javascript
ES6知识点整理之模块化的应用详解
Apr 15 #Javascript
详解如何运行vue项目
Apr 15 #Javascript
vue单页面在微信下只能分享落地页的解决方案
Apr 15 #Javascript
You might like
《PHP边学边教》(02.Apache+PHP环境配置――下篇)
2006/12/13 PHP
php伪静态之APACHE篇
2014/06/02 PHP
php实现给二维数组中所有一维数组添加值的方法
2017/02/04 PHP
PHP后台微信支付和支付宝支付开发
2017/04/28 PHP
Linux下 php7安装redis的方法
2018/11/01 PHP
JavaScript 语言的递归编程
2010/05/18 Javascript
读jQuery之十二 删除事件核心方法
2011/07/31 Javascript
javascript设计模式 封装和信息隐藏(上)
2012/07/24 Javascript
顶部缓冲下拉菜单导航特效的JS代码
2013/08/27 Javascript
网站接入QQ登录的两种方法
2014/07/22 Javascript
jquery专业的导航菜单特效代码分享
2015/08/29 Javascript
AngularJs定制样式插入到ueditor中的问题小结
2016/08/01 Javascript
vue.js学习笔记之绑定style样式和class列表
2016/10/31 Javascript
JavaScript面试题(指针、帽子和女朋友)
2016/11/23 Javascript
jquery网页日历显示控件calendar3.1使用详解
2016/11/24 Javascript
webpack配置打包后图片路径出错的解决
2018/04/26 Javascript
详解vue项目中如何引入全局sass/less变量、function、mixin
2018/06/02 Javascript
js实现简单模态框实例
2018/11/16 Javascript
nodejs实现UDP组播示例方法
2019/11/04 NodeJs
JS 事件机制完整示例分析
2020/01/15 Javascript
Angular8 简单表单验证的实现示例
2020/06/03 Javascript
用Python实现一个简单的能够发送带附件的邮件程序的教程
2015/04/08 Python
Linux上安装Python的PIL和Pillow库处理图片的实例教程
2016/06/23 Python
Django1.7+python 2.78+pycharm配置mysql数据库
2016/10/09 Python
通过python连接Linux命令行代码实例
2020/02/18 Python
Jupyter Notebook输出矢量图实例
2020/04/14 Python
python推导式的使用方法实例
2021/02/28 Python
元旦晚会策划方案
2014/02/18 职场文书
涉密人员保密承诺书
2014/05/28 职场文书
领导四风问题整改措施思想汇报
2014/10/13 职场文书
2015年中学校长工作总结
2015/05/19 职场文书
小爸爸观后感
2015/06/15 职场文书
工作经历证明范本
2015/06/15 职场文书
升学宴家长答谢词
2015/09/29 职场文书
Django开发RESTful API实现增删改查(入门级)
2021/05/10 Python
mysql使用FIND_IN_SET和group_concat两个方法查询上下级机构
2022/04/20 MySQL