How JavaScript Timers Work


本文翻译自https://johnresig.com/blog/how-javascript-timers-work/ 版权归作者所有,仅供学习与参考使用。有些知识时间长了忘记了,建议自己记录下来。
前一篇文章讲诉了microtask与macrotask,结合这篇文章会对JS的执行理解更为透彻!

从根本上讲,理解JavaScript计时器的工作原理非常重要。通常情况下,它们的行为是不直观的,因为它们是单线程。让我们从可以构造和操作计时器的三个函数开始。

  • var id = setTimeout(fn,delay) ; 初始化一个定时器并在指定的时间内调用回调函数。该函数会返回一个唯一的定时器 ID,我们剋在之后通过此ID 清除这个定时器
  • var id = setInterval(fn,delay);和setTimeout类似,但是会在每一个延时后持续的调用fn,直至这个定时器被清除
  • clearInterval(id);clearTimeout(id); 传入的参数是定时器ID,并会停止计时器的回调(非直接中断函数)

为了更进一步的理解计时器内部是如何工作的,有一个重要的概念需要认识:计时器延迟并不可靠timer delay is not guaranteed)。由于浏览器中的所有JavaScript单线程执行的,因此异步事件(如鼠标单击和计时器)在有空闲时执行。用图表最好地说明了这一点,如下所示:
在这里插入图片描述

图中有很多信息需要领会,但是完全理解它将使您更好地了解JavaScript异步执行流程。这个图是线性的:垂直方向表示时间,以毫秒为单位。蓝色框表示正在执行的JavaScript部分。例如,第一个JavaScript块大约执行18ms,鼠标单击块大约执行11ms,等。

由于JavaScript一次只能执行一段代码(由于它的单线程性质),这些代码块中会“阻塞”了其他异步事件的进程。这意味着当异步事件发生时(如鼠标单击、计时器触发或XMLHttpRequest完成)时,它将排队等待稍后执行(各浏览器对排队实现可能各有差异)。

首先,在第一个JavaScript块中,会启动两个计时器:10ms setTimeout和10ms setInterval。无论计时器是在何时何地启动的,都会在完成第一个代码块之前被触发(JavaScript)。但是,它不会立即执行(由于单线程,它无法执行)。取而代之,延迟函数将会被排队,以便在下一个可用时刻执行(task)。

除此之外,在第一个JavaScript块中,有mouse click点击事件。与此异步事件关联的JavaScript回调(我们永远不知道用户什么时候会执行某个操作,因此认为它是异步的)无法立即执行,因此,与初始计时器一样,让它排队等待稍后执行。

在JavaScript的初始块完成执行后,浏览器立即会问这样一个问题:等待执行的是什么(what waiting)?在这种情况下,鼠标单击处理程序和计时器回调都在等待。然后浏览器选择(鼠标单击回调)并立即执行它。计时器将等待到下一个可能的时间,以便执行。

注意,虽然鼠标单击处理程序正在执行第一个interval回调执行。与计时器一样,它的处理程序排队等待稍后执行。但是,当interval再次触发时(当计时器处理程序执行时),此处理程序执行将被删除。如果您要在执行大量代码时对所有Interval回调进行排队,那么在完成时,结果将是一系列Interval执行,它们之间没有延迟。相反,浏览器倾向等到没有更多的Intereval处理程序排队。

实际上,我们可以看到,当第三个Interval回调触发时,这个Interval本身正在执行。这向我们展示了一个重要的事实:Interval不在乎当前正在执行什么,它们将不分青红皂白地排队,即使这意味着回调之间的时间将被牺牲。

最后,在执行完第二个interval回调之后,我们可以看到JavaScript引擎已经没有什么可执行的了。这意味着浏览器现在等待一个新的异步事件发生。当Interval再次触发时,我们在50ms标记处得到这个。然而,这一次,没有任何东西阻止它的执行,因此它立即触发。

让我们看一个例子来更好地说明setTimeout和setInterval之间的区别。

setTimeout(函数(){
     /*一些长的代码块…* /
    setTimeout(arguments.callee,10);
},10);

setInterval(函数(){
     /*一些长的代码块…* /
},10);

乍一看,这两段代码在功能上可能是相同的,但实际上并非如此。值得注意的是,setTimeout代码在前一次回调执行之后总是会有至少10ms的延迟(它可能最终会更多,但绝不会更少),而setInterval会尝试每10ms执行一次回调,而不管上一次回调是什么时候执行的。

我们在这里学到了很多,让我们来回顾一下:

  • JavaScript引擎是单线程,迫使异步事件排队等待执行
  • setTimeout和setInterval在执行异步代码的方式上是完全不同的。
  • 如果一个计时器被阻止立即执行,它将被延迟到下一个可能的执行点(比预期的延迟要长)。
  • 如果执行的时间足够长(超过指定的延迟),Interval可以连续执行,而不会延迟。
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值