JavaScript事件队列和设置超时(...)

weylhg0b  于 2023-01-16  发布在  Java
关注(0)|答案(3)|浏览(145)

这段代码发生了什么

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。
那么,发生了什么事?

xurqigkl

xurqigkl1#

首先,setTimeout()是异步和非阻塞的,这意味着当你调用它时,它所做的只是注册新的计时器,然后立即返回,所以当你的代码运行时,它将遵循以下步骤:
1.将func1定时器注册为500ms。
1.将func2定时器注册为250ms。
1.每个计时器都被添加到事件循环的计时器部分内的排序数据结构中,因此事件循环很容易知道并检查下一个要执行的计时器。
1.启动while自旋循环,该循环运行大约1000ms。
1.当while spin循环结束时,Javascript解释器返回到事件循环,检查是否还有其他事情要做。(事件循环的一个阶段)是查看是否有任何计时器准备运行。它检查计时器列表的头部,并查看其时间是否已经过去(意味着它的运行时间已过)。因此,它获取与该func2计时器关联的回调并执行它。func2计时器位于列表的开头,因为它具有与其关联的最早时间。
1.当回调函数执行完毕时,JS解释器再次将控制权返回到事件循环,并再次检查是否没有其他事情要做。它会发现func1计时器现在位于列表的最前面,并且已经过了运行时间,因此它会获取与该计时器关联的回调函数并执行它。
因此,您可以看到func2回调在while自旋循环完成后立即执行,然后func1回调在此之后执行的输出。
注意,这些定时器是纯事件驱动的。没有中断当前执行的Javascript来执行定时器。因此,Javascript中的定时器是一种最佳的实现方式。如果您将定时器设置为从现在起1000ms,则与该计时器相关的回调将不早于1000ms运行,并且如果JS解释器在指定时间忙碌,则可能晚于1000ms。那个计时器。
定时器的一个方面在上面的描述中缺失(因为在您的情况下不会发生)是在没有while()自旋循环的情况下发生的。在这种情况下,您的代码在配置完两个计时器后立即将控制返回给事件循环,事件循环检查是否有计时器准备好运行,但是还没有一个准备好运行。然后它会一直休眠,直到有东西再次唤醒它,但是休眠的时间不会超过到下一个当前设置的计时器的时间。

euoag5mw

euoag5mw2#

不,等等,它不会把你的超时调用添加到事件队列中,它是由浏览器的webapi处理的,然后当你的超时启动时,它会把你的函数添加到事件队列中。
看看这个(https://www.youtube.com/watch?v=8aGhZQkoFbQ#t=13m从13:00开始)

du7egjpx

du7egjpx3#

Javascript是单线程的,即使有异步回调,它们也不是并发的。
这意味着,只有在整个代码执行完毕后,才会调用(setTimeout(fn,num))中的代码。因此,如果num参数= 5000,则函数setTimeout(...)将运行:在整个代码(即不仅仅是到setTimeout被调用时为止的代码,而是所有代码)执行完毕后+ 5秒。2希望这能有所帮助。

相关问题