【问题标题】:Understanding of Javascript code execution order理解Javascript代码执行顺序
【发布时间】:2016-04-01 05:16:13
【问题描述】:

我有一段 Javascript 代码。

var i, _i, _len, _ref;

_ref = [1, 2, 3];
for (_i = 0, _len = _ref.length; _i < _len; _i++) {
  i = _ref[_i];
  setTimeout((function() {
    return console.log(i);
  }), 0);
}

它是由一段 Coffeescript 生成的,这就是其中奇怪名称的原因。当您执行代码时,输​​出将是3 3 3。根据执行结果,执行顺序似乎是:

enter for loop
settimeout 1 with a function
settimeout 2 with a function
settimeout 3 with a function
settimeout 1 execute
settimeout 2 execute
settimeout 3 execute

这与我的理解有点不同。据我了解,for 循环中的setTimeout 是异步运行的。 setTimeout 语句本身可以立即完成。几毫秒后(在我们的例子中为 0),函数将开始在单独的上下文中执行。所以我预期的执行顺序是:

enter for loop
settimeout 1 with a function
settimeout 2 with a function, settimeout 1 execute
settimeout 3 with a function, settimeout 2 execute
settimeout 3 execute

根据 CPU 负载和传递给setTimeout 的函数的复杂性,执行顺序可能与我上面描述的不同。但我的观点是, setTimeout 可以立即开始执行,无需等待 for 循环完成。

希望有人可以帮助澄清。我检查了 MDN 的 setTimeout,但没有得到任何有用的东西。

【问题讨论】:

    标签: javascript settimeout execution


    【解决方案1】:

    对我来说,最好的解释方式是改变对 JavaScript 中任何异步调用的理解,尤其是 setTimeout。与其将setTimeout 视为直接函数调用,不如将其视为将一些事件添加到队列中,只有在其他所有操作完成后才会开始运行所有事件。

    在你认为这只是添加事件之后,你就会明白它在 JavaScript 中是如何自然地工作的。 JavaScript 应用程序是单线程应用程序(大部分时间)。这意味着只要您的同步代码在工作,它甚至不会开始运行异步请求,因此甚至不会触及队列。

    换句话说,setTimeout(..., 0) 正在添加某种“onSyncCodeDone”事件。

    获得预期执行顺序的唯一方法是使用递归:

    var _ref = [1, 2, 3];
    
    function process(index) {
      if (index < _ref.length) {
        setTimeout((function() {
          process(index + 1);
    
          return console.log(index);
        }), 0);
      }
    }
    
    process(0);
    

    【讨论】:

    • 除了setTimeout和setInterval之外,我可以考虑所有其他同步代码吗?
    • 不,所有 ajax 调用也是异步的。在 nodejs 中,近一半的函数是异步的 :)
    【解决方案2】:

    有一个事件循环的想法。即使 setTimeout 设置为 0(它确实不能低于 14ms 左右),它仍然被放置在事件循环中。因此,您希望它立即触发,但它可能不取决于上下文之外发生的其他情况,例如,循环运行。

    看看这个网站,它会让你大吃一惊,这正是你需要了解的内容。

    http://latentflip.com/loupe/

    【讨论】:

    • 谢谢詹姆斯,这个视频非常有用!
    【解决方案3】:

    我会尽量简单。 JavaScript 是一种单线程语言,这意味着在一个时间实例中只会发生单个操作。

    settimeout 2 with a function, settimeout 1 execute
    

    上面写的执行顺序是不可能的,两条语句不会同时执行。 JavaScript中有一个执行队列,一个一个地执行语句 根据您上面编写的代码,它的工作顺序如下:

    1. 您的 for 循环进入队列并开始运行
    2. SetTimeout 函数在指定时间后添加到队列中,在您的情况下为 0 毫秒。
    3. 一旦线程从 for 循环中释放出来,SetTimeouts 就会从队列中移除并执行。

    这里有一些可能有用的链接

    http://ejohn.org/blog/how-javascript-timers-work/

    http://javascript.info/tutorial/settimeout-setinterval

    【讨论】:

      【解决方案4】:

      问题是 setTimeout 将在循环执行后执行。因为那不是同步的。所以 setTimeout 里面的函数会一直把 i 的值取为 3。

      这将是正确的代码...

      var i, _i, _len, _ref;
      
      _ref = [1, 2, 3];
      for (_i = 0, _len = _ref.length; _i < _len; _i++) {
        i = _ref[_i];
        setTimeout((function(i) {
          console.log(i);
        })(i), 0);
      }
      

      【讨论】:

      • 我知道正确的密码。我只是想了解执行顺序。
      【解决方案5】:

      我在这里简化了循环,你可以在这里看到结果: https://jsfiddle.net/TomKarachristos/L6couu2o/

      for ( _i = 0, _len = 3; _i < _len; _i++) {
        setTimeout((function() {
          makeAParagraph(_i);
        }), 0);
      }
      

      当一段代码执行时,没有任何东西可以中断执行,所以所有的循环都在执行,没有一个超时运行。

      在该执行中,每次发现超时时,它都会将第一个参数(一个函数)推入回调队列中。如下图。

      当循环结束并且没有代码执行 JavaScript 时,转到回调队列找到第一个函数并执行它。如果这是超时,首先检查您作为第二个参数输入的时间是否通过(0 始终通过)。

      所以第一次超时它在循环结束时运行,当这种情况发生时 i 为 3。这里我们有另一种 JavaScript 技术,称为闭包,JavaScript 中的函数可以访问函数外部定义的变量。所以timeout里面的函数可以访问外面的变量i。但是当第一次超时被调用时,i 是 3,如图所示,第二次和第三次超时也是如此。

      【讨论】:

        猜你喜欢
        • 2012-02-26
        • 2018-12-15
        • 2021-03-16
        • 2018-10-02
        • 2017-07-12
        • 2012-04-21
        • 1970-01-01
        • 2014-05-26
        相关资源
        最近更新 更多