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

所属分类: 网络编程 / JavaScript 阅读数: 1756
收藏 0 赞 0 分享

这是重学 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 中详解整合,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对脚本之家网站的支持!

更多精彩内容其他人还在看

AngularJs IE Compatibility 兼容老版本IE

本文主要介绍AngularJs IE Compatibility 兼容老版本IE的问题及解决办法,有兴趣的小伙伴可以参考下
收藏 0 赞 0 分享

AngularJs Modules详解及示例代码

本文主要介绍AngularJs Modules的相关知识,这里整理了详细的资料及简单示例代码,有兴趣的朋友可以参考下
收藏 0 赞 0 分享

AngularJs Scope详解及示例代码

本文主要介绍AngularJs Scope的知识,这里整理了详细资料及示例代码,有兴趣的小伙伴可以参考下
收藏 0 赞 0 分享

node.js中module.exports与exports用法上的区别

Node.js 引入了模块(Module)概念,一个模块可以通过module.exports 或 exports 将函数、变量等导出,以使其它 JavaScript 脚本通过require() 函数引入并使用。那么node.js中module.exports与exports有什么
收藏 0 赞 0 分享

基于JS实现发送短信验证码后的倒计时功能(无视页面刷新,页面关闭不进行倒计时功能)

这篇文章主要介绍了基于JS实现发送短信验证码后的倒计时功能(无视页面刷新,页面关闭不进行倒计时功能)的相关资料,非常不错,具有参考借鉴价值,需要的朋友可以参考下
收藏 0 赞 0 分享

基于jQuery实现发送短信验证码后的倒计时功能(无视页面关闭)

最近做了一个项目,其中有需求要求实现发送短信验证码后倒计时功能,其中有个难点:要求关闭页面也进行倒计时。好吧,下面小编把jquery 发送验证码倒计时的实现代码分享给大家,大家可以参考下
收藏 0 赞 0 分享

js绘制购物车抛物线动画

这篇文章主要为大家详细介绍了js绘制购物车抛物线动画,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
收藏 0 赞 0 分享

vue.js入门教程之绑定class和style样式

小编之前介绍了通过vue.js计算属性,不知道大家都学会了吗。那这篇文章中我们将一起学习vue.js实现绑定class和style样式,有需要的朋友们可以参考借鉴。
收藏 0 赞 0 分享

纯JS实现可拖拽表单的简单实例

下面小编就为大家带来一篇纯JS实现可拖拽表单的简单实例。小编觉得挺不错的,现在就分享给大家,也给大家做个参考。一起跟随小编过来看看吧
收藏 0 赞 0 分享

js实现StringBuffer的简单实例

下面小编就为大家带来一篇js实现StringBuffer的简单实例。小编觉得挺不错的,现在就分享给大家,也给大家做个参考。一起跟随小编过来看看吧
收藏 0 赞 0 分享
查看更多