JavaScript中Array的forEach,Filter和

作者: keith666 | 来源:发表于2018-12-08 17:12 被阅读144次

    原文链接: https://www.jianshu.com/p/dbbd79e43c02

    对于Array来说,里面自带的forEach,filter,map等方法给我们带来了很多便利,但如果子语句中有异步操作,要怎么使用呢?直接在加async await?万万不可, 这里面还是有坑的,下面来一起讨论.

    Array.forEachasync结合使用的坑

    Array.forEach的常规使用

    var array1 = ['a', 'b', 'c'];
    
    array1.forEach(function(element) {
      console.log(element);
    });
    // output:
    a
    b
    c
    

    Array.forEachasync结合使用的坑

    // 模拟异步函数,如文件读写等I/O操作
    async function fAsync(i) {
      let result = await new Promise(r => setTimeout(() => r(i), 1000));
      return result;
    }
    async function fAction(item) {
      let asyncResult = await fAsync(item);
      item = asyncResult * item;
      console.log('f action: ' + item);
    }
    // 
    async function fForEach() {
      let list = [1, 2, 3];
      await list.forEach(fAction);
      console.log('after loop');
    }
    fForEach();
    // output
    after loop // 这边暂停一会然后再输出后面的结果
    f action: 1
    f action: 4
    f action: 9
    
    // expected
    f action: 1
    f action: 4
    f action: 9
    after loop // 这边暂停一会然后再输出后面的结果
    

    分析

    上述代码先执行了list.forEach(fAction)后面的console语句,然后forEach里面的fAction等了一会后才结束,这个是为什么呢?

    可以看下forEach类似的源码(按照ECMAScript标准):

    // callback相当于之前的fAction
    function forEach(array, callback) {
      if (array) {
        for (var i = 0; i < array.length; i++) {
          // 关键在这里,没有await
          var result = callback(array[i], i);
          if (result) {
            return result;
          }
        }
      }
      return undefined;
    }
    

    取自typescript

    这样就清楚了,由于forEach内部的实现中没有调用await,所以上面的console语句会在异步子函数之前执行,从而导致输出的结果,具体流程如下:

    forEach()->for循环->结束(不管异步语句执行情况)->console语句->异步语句执行结束
    

    JavaScript: async/await with forEach()

    Array.mapasync结合使用的坑

    async function fMap() {
      let list = [1, 2, 3];
      // 返回的判断结果会是Promise对象
      let newList = await list.map(num => num * num);
      console.log('after loop');
      console.log(list);
      console.log(newList);
    }
    fMap();
    
    // output
    after loop
    [ 1, 2, 3 ]
    [ Promise { 1 }, Promise { 4 }, Promise { 9 } ]
      
    // expected
    after loop
    [ 1, 2, 3 ]
    [ 1, 4, 9 ]
    

    Array.filterasync结合使用的坑

    async function fFilter() {
      let list = [1, 2, 3];
      // fiter中的判断结果返回的一直都是Promise对象,所以都是true
      let newList = await list.filter(async item => false);
      console.log('after loop');
      console.log(list);
      console.log(newList);
    }
    // output
    after loop
    [ 1, 2, 3 ]
    [ 1, 2, 3 ] 
    
    // expected
    after loop
    [ 1, 2, 3 ]
    [ ] 
    

    这个是很容易被忽略的错误,因为从结果的结构上看是完全正常的,如果filter的条件更加复杂一点则要识别的难度将更大.

    结论

    对于Array如果要在子语句中使用async语句,还是乖乖的用for循环吧,传统的for语句或新的for...infor...of都行.

    相关文章

      网友评论

        本文标题:JavaScript中Array的forEach,Filter和

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