JavaScript事件队列和setTimeout(..)

JavaScript Event Queue and setTimeout(...)

本文关键字:setTimeout 事件队列 JavaScript      更新时间:2023-09-26

代码发生了什么

var start = new Date();
setTimeout(function() {
    var end = new Date();
    console.log('Time elapsed with func1:',end - start, 'ms');
}, 500);
setTimeout(function() {
    var end = new Date();
    console.log('Time elapsed with func2:',end - start, 'ms');
}, 250);
while (new Date() - start < 1000) {}

日志:

Time elapsed with func2: 1001 ms
Time elapsed with func1: 1017 ms

我预计func1将首先被激发,因为它是第一个添加到事件队列中的事件。然后,由于JS的单线程特性,请等待func1返回,然后执行队列中的下一个事件func2
那么,发生了什么?

第一次关闭setTimeout()是异步且无阻塞的。这意味着当你调用它时,它所做的只是注册新的计时器,然后立即返回。因此,当您的代码运行时,它将遵循以下步骤:

  1. 您注册func1定时器500ms
  2. 您将func2计时器注册250毫秒
  3. 每个定时器都被添加到事件循环的定时器部分内的排序数据结构中,因此事件循环很容易知道并检查下一个要执行的定时器
  4. 开始while旋转循环。这个循环大约持续1000毫秒
  5. while旋转循环结束时,Javascript解释器可以返回事件循环,检查是否还有其他事情要做。它检查的一件事(事件循环的一个阶段)是查看是否有任何定时器可以运行。它检查计时器列表的头部,并看到它的时间已经过去了(这意味着它已经过了运行时间)。因此,它获取与func2计时器关联的回调并执行它。func2计时器位于列表的开头,因为它具有与之关联的最早时间
  6. 当该回调完成执行时,JS解释器再次将控制权返回到事件循环,在那里它再次检查是否没有其他事情要做。它会发现func1计时器现在位于列表的顶部,已经过了运行时间,因此它会获取与该计时器相关联的回调并执行它

因此,一旦while旋转循环完成,就会得到func2回调执行的输出,然后func1回调执行。

请注意,这些计时器完全由事件驱动。没有中断当前正在执行的Javascript来执行计时器。出于这个原因,Javascript中的计时器是一种尽力而为的实现方式。如果从现在起将计时器设置为1000ms,那么与该计时器关联的回调将不早于1000ms运行,并且如果JS解释器在该计时器的指定时间正忙,则可能会晚于1000ms。

上面的描述中缺少定时器的一个方面(因为在您的情况下不会发生)是,如果没有while()旋转循环,会发生什么。在这种情况下,您的代码在配置完两个计时器后立即将控制权返回到事件循环,事件循环会检查是否有计时器可以运行,但没有计时器可以运行。然后它会一直睡眠,直到有东西再次唤醒它,但睡眠时间不会超过下一个当前设置的计时器。

不,等等。它不会将您的超时调用添加到事件队列中。它由浏览器的webapi处理,然后当您的超时开始时,它会将您的函数添加到事件队列中。

这是什么:https://www.youtube.com/watch?v=8aGhZQkoFbQ#t=13m(从13:00开始)

Javascript是单线程的。即使有异步回调,它们也不是当前的。

这意味着,只有在整个代码完成执行后,才会调用(setTimeout(fn,num))中的代码。因此,如果num参数=5000。这意味着,函数setTimeout(…)将运行:在整个代码(即,不仅是到调用setTimeout时为止的代码,而且是所有代码)执行完毕+5秒之后。希望这能有所帮助。