[翻译]How JavaScript Timers Work

写在前面,这篇博客,哪里翻译的不好请见谅。(ORZ ,瑟瑟发抖),巨佬(John Resig) 原文

从一种基本层面上去理解JavaScript计时器是如何工作的是非常有必要的。因为它们处于单线程的环境里,因此它们的行为并不是那么直观。开始通过测试我们能用来构造并且操控计时器的三个函数开始吧。

  • var id = setTimeout(fn, delay); --初始化一个计时器,其会在一段时间间隔后调用的特殊函数。这个函数会返回一个唯一的ID,计时器在之后可以通过ID取消。
  • var id = setInterval(fn, delay); --与setTimeout很像,但是会继续调用函数(每时延一段时间)直到它被取消。
  • clearInterval(id); clearTimeout(id); --接收一个计时器ID(被上述任一函数所返回)并且中断ID对应的计时器。

为了从本质上理解计时器的工作原理,我们需要深究一个重要的概念:计时器时延不可靠。因为所有浏览器中的JavaScript都在单线程下执行,异步事件(例如鼠标点击和计时器)只有在执行中存在空隙入口才能运行。最好用一个图表来示范,如下所示:

点击预览大图

这个图中有大量的信息需要理解消化,但是完整的理解它将会给你对于异步的JavaScript如何工作执行有一个清晰的认知。这个图表是一维的:我们有一个竖直的毫秒时刻表(挂钟)。蓝色的块代表正在执行的JavaScript的一部分。举个例子,第一块JavaScript执行了大约18ms,鼠标点击模块执行了大约11ms,诸如此类。

因为JavaScript在一个时刻永远只能执行代码的一部分(由于它的单线程本质)每一个在代码中像这样的块都“阻塞”其他的异步事件的进程。这意味着当异步事件发生(类似一个鼠标点击、激活计时器、或者一个XMLHttpRequest完成),它会排成队列在之后被逐个执行(这个队列设置是如何确切的发生在不同的浏览器上各自不同,所以别想复杂了)。

首先,在第一个JavaScript块中,两个计时器被初始化:一个10ms的setTimeOut和一个10ms的setInterval。在我们真正完成第一块代码之前就决定了计时器何时何地开始激活。需要注意的是,无论怎样,绑定的函数都不会立刻执行(由于线程的原因,没有权利去这样做)。取而代之的是让函数延迟组成队列,为了在下一个空闲的时刻执行。

此外,在这第一个JavaScript块里面我们看到了一个鼠标点击事件产生了。JavaScript回调函数链接了这异步事件(我们永远不知道用户何时可以执行某个操作,因此它被看做是异步的),这个函数无法立刻执行,就像初始计时器一样,会排进队列之后被执行。

在初始的JavaScript块执行结束之后,浏览器立刻提出问题:什么东西等待被执行?在那个时候一个鼠标点击处理程序和一个计时器回调都处于等待中。然后浏览器挑选一个(鼠标点击回调)并且立即执行它。计时器为了被执行会继续等待直到下一个可能的时刻。

需要注意的是,当鼠标点击事件处理程序正在执行的时候,第一个定时器回调也在执行。于是计时器的处理程序进入队列等待下次执行。但是,当定时器再一次被激活时(当计时器的处理程序正在执行时),这一次定时器的处理执行程序被丢弃了。如果你将所有定时器的回调整理成队列形式,在一大块JavaScript代码执行完成得到结果之后,这些定时器会彼此没有时间间隔的执行。除此之外,浏览器会倾向于等待执行队列里面没有定时器的处理程序(产生定时器相关的问题)之后再往队列里面添加定时器。

事实上,我们可以看到当第三个定时器回调函数开始激活时,定时器本身也在执行。这告诉我们一个重要的现象:定时器不关心谁正在执行,它们会任意的排进队列,那意味着回调之前的时间间隔会被挤压掉。

最后,在第二次定时器回调完成了执行,我们能看到JavaScript引擎没有什么需要去执行。这代表着浏览器现在等待着新的异步事件发生。我们在标记的50ms处,定时器又一次被激活,于是得到了新的异步事件。这一次,终于没有什么阻塞它的执行,于是它立即运行。

我们来看下一个例子来更好的诠释setTimeoutsetInterval之间的不同。

setTimeout(function(){
  /* Some long block of code... */
  setTimeout(arguments.callee, 10);
},10);
复制代码
setInterval(function(){
    /* Some long block of code... */
},10);
复制代码

这两段代码第一眼看上去可能功能上相同,实则不然。显然,setTimeout代码段在前回调执行后至少10ms执行(只会多,不会少)。setInterval会尝试每10ms执行,不管上一次回调是否执行完成。

我们从这儿学到了很多,总结一下。

  • JavaScript引擎只有一个线程,强制异步事件在执行队列里等待。
  • setTimeout和setInterval在执行异步代码有着根本性的不同。
  • 如果一个计时器被阻塞了,它会延后,直到下一次可能执行的时间点立即执行(可能会比预期的时间间隔要长)。
  • 定时器如果执行的时间长(比给定的间隔时间更长),它们将连续的执行没有时间间隔。

所有这些都是很重要的。清楚JavaScript引擎如何工作,尤其是在大量的异步事件发生时,为构造健壮的应用程序代码打下坚实的基础。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值