入冬了,回想起 2016 年的那个寒冬,我遭遇了裁人的风波。给我敲响了警钟,基础知识不能忘,时常要复习复习,才能应对一些突如其来的事情,今天篇文章也是由于以前被考到,记忆模糊回答的不是很肯定,因此提笔再次记录一番。javascript
ES7 引入了 async、await 方法,来处理异步操做。关于这俩方法的使用,不赘述,网上的文章多如牛毛,又写一篇入门,未免废话连篇。今天要分享的内容是在js的一些循环方法中,async 和 await 的表现是如何的。java
在 for 循环中,async 和 await 表现的仍是使人满意。如今假设一个场景,设置一个变量,爸妈我三我的的体重,求和数组
const family = { "dad": 150, "mom": 100, "son": 200 } const familyToGet = ["dad", "mom", "son"] // 写一个sleep方法 const sleep = ms => { return new Promise(resolve => setTimeout(resolve, ms)) } // 假设获取体重数据的过程是一个请求接口的异步过程 const getFamilyWeight = person => { return sleep(2000).then(() => family[person]) } const loop_for = async () => { console.log('start') let result = 0 for(let i = 0; i < familyToGet.length; i++) { const person = familyToGet[i] const weight = await getFamilyWeight(person) result += weight } console.log('result:', result) console.log('end') } loop_for() // 运行结果 // start // 150 // 100 // 200 // result: 450 // end 复制代码
按照上述代码的打印结果分析,for 循环内的代码,每一次都会等当前的 getFamilyWeight 方法返回了内容以后,才会继续下一个循环,循环所有结束的时候,才会跑 for 循环下面的代码,这是咱们想要的结果,nice。promise
在 forEach 中,async 和 await 表现的差强人意,具体缘由,还应归结于 forEach 中的回调函数,具体看看代码的表现。markdown
const family = { "dad": 150, "mom": 100, "son": 200 } const familyToGet = ["dad", "mom", "son"] // 写一个sleep方法 const sleep = ms => { return new Promise(resolve => setTimeout(resolve, ms)) } // 假设获取体重数据的过程是一个请求接口的异步过程 const getFamilyWeight = person => { return sleep(2000).then(() => family[person]) } const loop_forEach = () => { console.log('start') let result = 0 // 在回调函数中,异步是很差控制的 familyToGet.forEach(async person => { const num = await getFamilyWeight(person) console.log(num) result += num }) console.log('result', result) console.log('end') } loop_forEach() // 运行结果 // start // result 0 // end // 150 // 100 // 200 复制代码
从运行结果中,能够看出,代码没有等待forEach方法执行结束,而是直接继续日后执行了。回调函数传进去以后,2秒钟以后才会返回数据,这不是咱们想要的结果,我如今是但愿它能顺序的执行。异步
那么咱们分析一下 forEach 内部大体的原理async
Array.prototype.forEach = function(cb) { // this 为当前调用该函数的变量 for(let i=0; i < this.length; i++) { cb(this[i], i); } } 复制代码
且看上面的简易 forEach 重写,其实内部也是一个 for 循环,那么为何不能表现的和 for 循环同样的运行结果呢?函数
咱们的回调函数进入 forEach 以后,又被单独的以 cb() 这种形式执行了一次,至关于在内部又建立了一个 async 、await 形式的方法,当 forEach 函数执行完的时候,至关于建立了 3 个方法,这 3 个方法须要等待2 秒钟,数据才会返回,而 forEach 外面的代码是不会等这 3 个函数返回内容再执行,而是独断独行的管本身走了,因此才会形成这样的局面。返回问题的根本,我如今的需求是想要代码能顺序执行,且最后能算出一家人体重之和,下面咱们来修改一下代码。oop
const family = { "dad": 150, "mom": 100, "son": 200 } const familyToGet = ["dad", "mom", "son"] // 写一个sleep方法 const sleep = ms => { return new Promise(resolve => setTimeout(resolve, ms)) } // 假设获取体重数据的过程是一个请求接口的异步过程 const getFamilyWeight = person => { return sleep(2000).then(() => family[person]) } const loop_forEach = async () => { console.log('start') let promise = [] // 在回调函数中,异步是很差控制的 familyToGet.forEach(person => { const num = getFamilyWeight(person) promise.push(num) }) const result = await Promise.all(promise) console.log('result', result) const weight = result.reduce((sum, personWeight) => sum + personWeight) console.log('weight', weight) console.log('end') } loop_forEach() // 运行结果 // start // result [150, 100, 200] // weight 450 // end 复制代码
在 forEach 的回调函数内,直接把 getFamilyWeight 方法返回的 promise 对象 push 到 promise 这个数组变量内,经过 Promise.all 来作一层等待的异步处理。this
在 map 中,直接返回一个 promise 数组
const family = { "dad": 150, "mom": 100, "son": 200 } const familyToGet = ["dad", "mom", "son"] // 写一个sleep方法 const sleep = ms => { return new Promise(resolve => setTimeout(resolve, ms)) } // 假设获取体重数据的过程是一个请求接口的异步过程 const getFamilyWeight = person => { return sleep(2000).then(() => family[person]) } const loop_map = async () => { console.log('start') // map中返回的是一个promise数组,须要经过Promise.all处理 const promise = familyToGet.map(async person => { const num = await getFamilyWeight(person) return num; }) console.log('promise', promise) // 等待promise里的内容所有返回,才会继续往下执行 const result = await Promise.all(promise) console.log('result', result) const weight = result.reduce((sum, personWeight) => sum + personWeight) console.log('weight', weight) console.log('end') } loop_map() // 运行结果 // start // promise [Promise, Promise, Promise] // result [150, 100, 200] // weight 450 // end 复制代码
你们应该能感受到其实这个和上面改版后的 forEach 差很少,没错,正如你所料,只不过 map 方法能返回新的数组罢了,forEach 则不能返回数组。
在这里把 map 的简单实现写一下,方便理解
Array.prototype.map = function(cb) { // this 为当前调用该函数的变量 var o = []; for(let i=0; i < this.length; i++) { var temp = cb(this[i], i); o.push(temp); } return o; } 复制代码
如上述代码,在外面经过 o 这个变量收集 cb() 回调函数的返回值,再到外面统一处理。真是妙啊,你们能够细细品味一番,能吾出不少新的对象。
一、for循环内使用async和await仍是能够的,稳如老狗🐶
二、不要在forEach方法内使用async、await,尽可能避免这种写法,坑啊。。。