意外行为:Javascript,setTimeout()和IIFE

时间:2019-01-02 02:30:39

标签: javascript closures settimeout iife

JavaScript,事件循环,setTimeout,IIFE,关闭

基于以下参考,我对以下代码的理解是:

setTimeout()是非阻塞的,并且由浏览器Web API处理,当完成计时器时,浏览器Web API将回调置于回调队列中。然后,事件循环等待调用堆栈释放以依次运行每个回调。 setTimeout闭包将关闭匿名IIFE,并且每次迭代都具有正确的index值。

for(var i = 0; i < 3; i++){
    (function(index){
        setTimeout(function(){
            console.log(index);
        }, 5000);
    })(i);
    console.log("loop="+i);
}
/*Output in console is
loop=0
loop=1
loop=2
//after 5 seconds
0
1
2
*/

我正在寻找解释以下代码在Chrome中发生的情况。

for (var i = 0; i < 3; i++) {
    setTimeout(
        function(index) { 
            console.log(index);
        }(i), 5000
    );
    console.log("loop="+i);
}
/* Output in console without any delay is:
0
loop=0
1
loop=1
2
loop=2
*/

为什么立即执行'console.log(index)'而没有5秒的延迟?

Web API如何以回调作为IIFE执行setTimeout()?

回调队列中是否有任何回调?

事件循环是否将任何回调移动到调用堆栈?

还是忽略setTimeout()并立即在调用堆栈上执行其回调?


我咨询过的参考文献:

Philip Roberts:无论如何,事件循环到底是什么? | JSConf欧盟2014 https://www.youtube.com/watch?v=8aGhZQkoFbQ

Philip Roberts帮助我陷入了2016年的事件循环 https://www.youtube.com/watch?v=6MXRNXXgP_0

调用堆栈和事件循环 https://www.youtube.com/watch?v=mk0lu9MKBto

JavaScript closure inside loops – simple practical example

Use IIFE in setTimeout in a loop, but why?

3 个答案:

答案 0 :(得分:2)

setTimeout(
    function(index) { 
        console.log(index);
    }(i), 5000
);

您正在调用传递给setTimeout 立即的第一个参数。当解释器遇到setTimeout行时,它首先尝试将其所有参数解析为值。第一个参数是函数调用,因此它会调用该函数以期望将解析为另一个函数-就像一个人可以做什么

setTimeout(makeFn('foo'), 5000);

其中makeFn返回一个函数。

因此,在您的代码中,

    function(index) { 
        console.log(index);
    }(i)

立即运行,但不返回任何内容-解释器将setTimeout行解析为

setTimeout(undefined, 5000);

但是undefined不是函数,因此没有异步排队。

您这里没有任何IIFE-将整个setTimeout行放在IIFE中:

for (var i = 0; i < 3; i++) {
  ((i) => {
    setTimeout(
      function() {
        console.log(i);
      }, 500
    );
    console.log("loop=" + i);
  })(i);
}

(或者,当然,使用constlet而不是var-最好避免使用var,其提升和功能范围非常不直观,并且需要冗长的解决方法像for循环中的这些)

for (let i = 0; i < 3; i++) {
  setTimeout(
    function() {
      console.log(i);
    }, 500
  );
  console.log("loop=" + i);
}

答案 1 :(得分:0)

在第二个示例中,您没有将函数传递给setTimeout,而是将其传递给函数调用的结果(在这种情况下为空)。

        function(index) { 
            console.log(index);
        }(i)

您看到,在此示例中,您的函数将立即调用,因此以后无需调用任何内容,并且控制台日志按顺序进行。

答案 2 :(得分:0)

  

我正在寻找以下内容的解释   Chrome中的代码。

for (var i = 0; i < 3; i++) {
    setTimeout(
        function(index) { 
            console.log(index);
        }(i), 5000
    );
    console.log("loop="+i);
}
/* Output in console without any delay is:
0
loop=0
1
loop=1
2
loop=2
*/

考虑以下语句:

function(index) { 
    console.log(index);
}(i)

这是一个匿名函数,并立即执行(最后的括号“()”执行该函数):请参见语法function(param) {...}()。这样的效果是,对于每次迭代,上面的代码都会立即执行。

结果是(如您所见):

0
1
2

setTimeout at MDN。方法期望功能(在计时器到期后 之后执行)或 code 作为其第一个参数。在这种情况下,您具有立即执行的代码(不是函数)。因此,您会立即看到结果。

5秒的延迟无效,它从未使用过。延迟后没有任何执行。

在Firefox中效果也是一样。

您可以在匿名函数末尾尝试不带括号的代码,然后看看会发生什么:

function(index) { 
    console.log(index);
}

在这种情况下,该功能将在五秒钟后执行!