Javascript 遍历中异步操作

引言

JS遍历中异步操作,指的是JS循环遍历中,每一次循环都有异步操作。 如经典的闭包应用,每次循环, 在异步操作中输出索引。


for (var i = 0; i < 3; i++) {
  setTimeout(() => {
    console.log('i', i);
  }, 10);
}
// i 3
// i 3
// i 3

结果发现,每次输出的都是3,并不是我们预期的结果。
不同遍历方法中异步操作,如何正确得到循环索引,这就是今天的主题。 当然,闭包就是其中一种解决方法。

解决方法

  • for循环 + var + 索引传参
for (var i = 0; i < 3; i++) {
  (function(i) {
    setTimeout(() => {
      console.log('i', i);
    }, 10);
  })(i);
}
// i 0
// i 1
// i 2

解析

每次循环, 都会执行一个匿名函数,这个匿名函数是立即执行函数(IIFE)。 每一次循环创建一个私有词法环境,执行时把当前的循环的i传入,保存在这个词法环境中。遍历完成后,js不会释放这块内存,所以能够正确的输出索引。


  • for循环 + var + 闭包

for (var i = 0; i < 3; i++) {
  var func = function() {
    var j = i;
    return function () {
      setTimeout(() => {
        console.log('i', j);
      }, 10);
    }
  }
  func()();
} 
// i 0
// i 1
// i 2

<!-- 或者 -->

for (var i = 0; i < 3; i++) {
  (function() {
    var j = i;
    setTimeout(() => {
      console.log('i', j);
    }, 10);
  })();
}
// i 0
// i 1
// i 2

解析

每次遍历操作中,定时器中函数都会用到父级的作用域的变量j,每次遍历都会形成一个闭包,不会释放变量j,所以每次能正确输出索引号。


  • for循环 + let
for (let i = 0; i < 3; i++) {
  setTimeout(() => {
    console.log('i', i);
  }, 10);
}
// i 0
// i 1
// i 2
<!-- 或者 -->
for (var i = 0; i < 3; i++) {
  let j = i;
  setTimeout(() => {
    console.log('i', j);
  }, 10);
}
// i 0
// i 1
// i 2

解析

let 是es6的一种新的变量声明方式,拥有块级作用域。上面代码中,变量i是let声明的,当前的i只在本轮循环有效,所以每一次循环的i其实都是一个新的变量,所以输出的是正确的索引。


  • for…of 和 for…in
for (var i in [0,1,2]) {
  (function(i) {
  setTimeout(() => {
    console.log('i', i);
  }, 10);
  })(i);
}
<!-- 或者 -->
for (let i in [0,1,2]) {
  setTimeout(() => {
    console.log('i', i);
  }, 10);
}
// i 0
// i 1
// i 2

解析

for…of 是es6中的一种遍历数组方式,for…in 大多数是遍历对象;在循环遍历中异步操作效果同for循环。


特殊遍历方式

  • forEach、map、filter
<!-- forEach遍历 -->
[0,1,2].forEach(i => {
  setTimeout(() => {
    console.log('i', i);
  }, 10);
});
// i 0
// i 1
// i 2

<!-- map遍历 -->
[0,1,2].map(i => {
  setTimeout(() => {
    console.log('i', i);
  }, 10);
});
// [undefined, undefined, undefined]  -- 每次遍历都返回
// i 0
// i 1
// i 2

<!-- filter遍历 -->
[0,1,2].filter(i => {
  setTimeout(() => {
    console.log('i', i);
  }, 10);
});
// []  -- 每次遍历为真时返回
// i 0
// i 1
// i 2

解析

这三个特殊遍历方式, 变量i的作用类似于let i。每次循环都有自己作用域,所以能够输出正确的索引。

猜你喜欢

转载自blog.csdn.net/bob_baobao/article/details/80183684
今日推荐