美文网首页
async/await 与循环for同时使用

async/await 与循环for同时使用

作者: any_5637 | 来源:发表于2020-04-26 11:41 被阅读0次

当async与循环同时使用,我们希望它能够在一个循环结束之后再继续下一步操作,现实中代码往往会出现一些问题:

var getNumbers = () => {
  return Promise.resolve([1, 2, 3])
}
var multi = num => {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      if (num) {
        resolve(num * num)
      } else {
        reject(new Error('num not specified'))
      }
    }, 1000)
  })
}
async function test () {
  var nums = await getNumbers()
  nums.forEach(async x => {
    var res = await multi(x)
    console.log(res)
  })
}
test()

问题:

在这个例子中,通过 forEach 遍历的将每一个数字都执行 multi 方法。代码执行的结果是:1 秒后,一次性输出1,4,9。这个结果和我们的预期有些区别,我们是希望每间隔 1 秒,然后依次输出 1,4,9;所以当前代码应该是并行执行了,而我们期望的应该是串行执行。

问题分析
我们知道,在js中循环遍历数组的方式有:
1、for

for(i = 0; i < arr.length; i++) {
  console.log(arr[i]);
}

2、for-in:for-in 语句以任意顺序遍历一个对象的可枚举属性,对于数组即是数组下标,对于对象即是对象的 key 值。注意 for-in 遍历返回的对象属性都是字符串类型,即使是数组下标,也是字符串 “0”, “1”, “2” 等等。

for (var index in myArray) {
  console.log(myArray[index]);
}

3、forEach:forEach 方法用于调用数组的每个元素,并将元素传递给回调函数;注意在回调函数中无法使用 break 跳出当前循环,也无法使用 return 返回值

myArray.forEach(function (value) {
  console.log(value);
});

4、for-of:for-of 语句为各种 collection 集合对象专门定制的,遍历集合对象的属性值

for (var value of myArray) {
  console.log(value);
}

5、还有其他遍历的方法有map、some、filter、等等,这里就不一一叙述了。

在本例中 forEach 的回调函数是一个异步函数,异步函数中包含一个 await 等待 Promise 返回结果,我们期望数组元素串行执行这个异步操作,但是实际却是并行执行了。
forEach 的 polyfill 参考:MDN-Array.prototype.forEach(),简单点理解:

  Array.prototype.forEach = function (callback) {
  // this represents our array
  for (let index = 0; index < this.length; index++) {
    // We call the callback for each entry
    callback(this[index], index, this)
  }
}

相当于 for 循环执行了这个异步函数,所以是并行执行,导致了一次性全部输出结果:1,4,9.

async function test () {
  var nums = await getNumbers()
//   nums.forEach(async x => {
//     var res = await multi(x)
//     console.log(res)
//   })
  for(let index = 0; index < nums.length; index++) {
    (async x => {
      var res = await multi(x)
      console.log(res)
    })(nums[index])
  }
}

那我们该如何解决呢?
方法:
改造forEach,确保每一个异步的回调执行完成后,才执行下一个:

async function asyncForEach(array, callback) {
  for (let index = 0; index < array.length; index++) {
    await callback(array[index], index, array)
  }
}
async function test () {
  var nums = await getNumbers()
  asyncForEach(nums, async x => {
    var res = await multi(x)
    console.log(res)
  })
}

相关文章

网友评论

      本文标题:async/await 与循环for同时使用

      本文链接:https://www.haomeiwen.com/subject/lxldwhtx.html