阅读 1977

关于JS的for循环包裹异步函数的问题

原文链接: www.jianshu.com

今天一个做后端的同事问了我一个JS的问题:

有个循环,循环一个异步回调,为啥回调引用的循环值都是最后一步循环的循环值?然后,又有些时候无论什么循环值都得不到?

好吧,JavaScript跟PHP的循环有时候确实不一样,JavaScript的函数有同步函数跟异步函数的区分,PHP里面没这种概念,拿PHP的常识来理解JavaScript有时候行不通。关于JS异步机制的研究,看我另一篇JS异步执行机制理解

我想了想,他说的“什么循环值也得不到的”,应该是下面这个情况:

<script type="text/javascript">
var arr = [1,3,5,7,9];
var arrLength = arr.length;

for (var i = 0; i < arrLength; i++) {
    setTimeout(function() {
        console.log(i);
        console.log(arr[i]);
    }, 2000);
}
</script>
复制代码

结果是:

5
undefined
5
undefined
5
undefined
5
undefined
5
undefined
复制代码

for循环有一个特点,就是“i判断失败一次才停止”。所以,i在不断的自加1的时候,直到i等于5,i才失败,这时候循环体不再执行,会跳出,所以i等于5没错。那么为什么5次循环的i都等于5?原因就是setTimeout()的回调,也就是console.log(i);console.log(arr[i]);被压到任务队列的最后,for循环是同步任务,所以先执行,等于是空跑了5次循环。于是,i都等于5之后,console.log(i);console.log(arr[i]);刚开始第一次执行,当然输出全是5。

然后,同事说,有时候JS的for循环,永远只得到最后一个循环值,那其实他用的是for...in...循环。具体不多解释了。

我既然听了他的问题,就要给他解决方案。

我先建议他用自执行函数传参,这样自执行函数内部形成了局部作用域,不受外部变量变化的影响。范例代码是:

<script type="text/javascript">
var arr = [1,3,5,7,9];
var arrLength = arr.length;

for (var i = 0; i < arrLength; i++) {
    (function(i) {
        setTimeout(function() {
            console.log('i是' + i);
            console.log('value是' + arr[i]);
        }, 2000);
    })(i);
}
</script>
复制代码

得到:

Paste_Image.png

不但解决了undefined的问题,而且解决了异步函数传参的问题。

然后我把范例代码给了他。然而,他的JS代码写的太乱,拿这个例子改居然改不对。于是我又给了一个jQuery方案给他:

<script type="text/javascript">
var arr = [1,3,5,7,9];

$.each(arr, function(key, value) {
    setTimeout(function() {
        console.log('i是' + key);
        console.log('value是' + value);
    }, 2000);
});
</script>
复制代码

用jQuery的$.each(),自带回调函数,形成了函数作用域,这娃最终解决了问题。

文章分类
前端
文章标签