JS循环中的中断、异步for...in、forEach、 map

5,064 阅读4分钟

Array.prototype.forEach

forEach方法对数组的每个元素执行一次提供的函数。 语法: arr.forEach(callback[, thisArg]); 参数:

  1. callback(currentValue, index?, array?) 问好表示可选
  2. thisArg可选参数。当执行回调函数时用作 this 的值(参考对象)。

关于异步

forEach是并行的对每个元素执行函数。所以await不会阻碍循环和代码的执行

  const arr = [1, 2, 3]
  async function wait (time) {
    const now = Date.now()
    return new Promise((res, rej) => {
      setTimeout(() => {
        console.log('我是异步执行的函数')
        res()
      }, time)
    })
  }

  arr.forEach(async (item) => {
    await wait(1000)
    console.log(item)
    return item * 2
  })
  console.log(arr) 

打印结果为:

原因:forEach中的异步函数不生效,所以会直接执行 console.log(arr),大约1秒后会执行完异步函数,然后执行对应的console.log(item).这里的执行顺序和浏览器的event loop机制相关。 如果想要确保异步代码执行完成后再继续执行,可以使用Promise.all。上面的代码可以改写成:

  const arr = [1, 2, 3]

  async function wait (time) {
    const now = Date.now()
    return new Promise((res, rej) => {
      setTimeout(() => {
        console.log('我是异步执行的函数')
        res()
      }, time)
    })
  }
  let asyncFuncs = []
  arr.forEach(async (item) => {
    asyncFuncs.push(wait(1000))
    console.log(item)
    return item * 2
  })
  Promise.all(asyncFuncs).then(() => {
    console.log(arr)
  })

注意点: forEach循环里不能有break或continue, 会产生报错 callback可以使用return但return的结果没有作用,只是提前结束函数运行,也就是calback总是返回undefined forEach返回值undefined

Array.prototype.map

map方法和forEach相似,但map方法会创建一个新数组,其结果是该数组中的每个元素都调用一个提供的函数后返回的结果。 参数:和forEach相同

注意点: 异步函数执行上map和forEach相同,await不生效 循环里不能有break或continue, 会产生报错 callback的return 返回新的数组元素,不使用return时等价于返回undefined

const array = [1, 4, 9, 16]
const map = array.map(x => x * 2)
console.log(map) // [2,8,18,32]

for

使用:for(let i = 0; i < 10; i += 1){}

关于异步函数

await在for循环中是生效的,即,循环会一个完成后继续执行下一个。

关于break和continue

for循环中可以使用break continue。break会结束循环,继续执行代码。continue是结束本次循环。

注意: for循环中没有return。如果使用会产生报错Uncaught SyntaxError: Illegal return statement

for...of

for...of语句在可迭代对象上创建一个迭代循环,执行语句。可迭代对象是指对象具有Iterator接口。也就是如果一个变量具有Iterator接口,就可以用for..in方法去循环遍历。Array和Map都是可迭代对象,但对象上是没有iterator接口的。如果想在对象上使用iterator方法,可以在对象的Symbol.iterator上写个Iterator函数。例如:

  const person = {
    name: 'Amy',
    age: 18,
    [Symbol.iterator] () {
      return Object.keys(this)[Symbol.iterator]()
    }
  }
  for (let item of person) {
    console.log(item)
  } // 控制台会输出 name 和 age

关于异步函数

和for循环一样,会等待await执行完后继续执行。例如:

  async function wait (time) {
    const now = Date.now()
    return new Promise((res, rej) => {
      setTimeout(() => {
        console.log('我是异步执行的函数')
        res()
      }, time)
    })
  }

  const arr = [1, 2, 3]

  async function test () {
    for (let i of arr) {
      console.log(i)
      await wait(1000)
    }
  }

  test()

执行结果:

关于continue和break

和for循环相同, break跳出循环;continue跳出本次循环继续执行 例如:

  const arr = [1, 2, 3]
  for (let i of arr) {
    if (i === 2) {break}
    console.log(i)
  } // 会输出1

注意: 没有return 会报错

for...in

异步执行

和 for 循环及for..of一样依次执行代码。

break和continue

忽略break和continue for...in中也没有return

总结:

  1. forEach和map,await不生效;使用break或continue会报错
  2. for循环、for...in,for...of,支持await,for和for...of中可以使用break和continue;for...in会忽略continue和break

开发过程中,根据使用场景,有些可以并行执行,有些情况需要依次执行一组异步函数。可以封装一个方法来执行。

  /*
  * 并行执行一组方法
  *  @params {Array<Object>} funcArr 一组待执行的函数{func: asyncFunc, params: funcParams}
  *  @returns {Array<Promise>} 函数执行结果(按数组顺序返回)
  * @example
  * excuteInParallel([{func: asynFuc1, params: 2},{func: asynFuc2, params: {param: 1}}}])
  * */
  let ret = []

  async function excuteInParallel (funcArr = []) {
    const result = funcArr.map((item) => {
      if (item.params) {
        return item.func(item.params)
      }
      return item.func()
    })
    return Promise.all(result)
  }
/*
* 串行执行一组异步方法
*  @params {Array<Object>} funcArr 一组待执行的函数{func: asyncFunc, params: funcParams}
*  @returns {Array<Promise>} 函数执行结果(按数组顺序返回)
* @example
* excuteInParallel([{func: asynFuc1, params: 2},{func: asynFuc2, params: {param: 1}}}])
* */
  async function excuteInSeries (funcArr = []) {
    const result = []
    for (const item of funcArr) {
      if (item.params) {
        result.push(await item.func(item.params))
      } else {
        result.push(await item.func())
      }
    }
  }