`
yeminping
  • 浏览: 181071 次
  • 性别: Icon_minigender_1
  • 来自: 福州
社区版块
存档分类
最新评论

JavaScript的单线程性质以及定时器的工作原理

阅读更多


最近在写JavaScript时遇到一些问题,就是当JavaScript多事件连续触发,JavaScript的单线程引擎是如何控制的。找了一些资料,觉得很有用,在此分享一下。

虽然不是原创,但是觉得此文章对JavaScript程序员非常有用。翻译的不是十分精确,但希望对大家有用。

原文:John Resig   http://ejohn.org/blog/how-javascript-timers-work/
How JavaScript Timers Work


从基础的层面来讲,理解JavaScript的定时器是如何工作的是非常重要的。计时器的执行常常和我们的直观想象不同,那是因为JavaScript引擎是单线程的。我们先来认识一下下面三个函数是如何控制计时器的。

    * var id = setTimeout(fn, delay); - 初始化一个计时器,然后在指定的时间间隔后执行。该函数返回一个唯一的标志ID(Number类型),我们可以使用它来取消计时器。
    * var id = setInterval(fn, delay); - 和setTimeout有些类似,但它是连续调用一个函数(时间间隔是delay参数)直到它被取消。
    * clearInterval(id);, clearTimeout(id); - 使用计时器ID(setTimeout 和 setInterval的返回值)来取消计时器回调的发生

为了理解计时器的内在执行原理,有一个重要的概念需要加以探讨:计时器的延迟(delay)是无法得到保障的。由于所有JavaScript代码是在一个线程里执行的,所有异步事件(例如,鼠标点击和计时器)只有拥有执行机会时才会执行。用一个很好的图表加以说明:



(点击查看大图)



在这个图表中有许多信息需要理解,如果完全理解了它们,你会对 JavaScript引擎如何实现异步事件有一个很好的认识。这是一个一维的图标:垂直方向表示时间,蓝色的区块表示JavaScript代码执行块。例如第一个JavaScript代码执行块需要大约18ms,鼠标点击所触发的代码执行块需要11ms,等等。

由于JavaScript引擎同一时间只执行一条代码(这是由于 JavaScript单线程的性质),所以每一个JavaScript代码执行块会“阻塞”其它异步事件的执行。这就意味着当一个异步事件发生(例如,鼠标点击,计时器被触发,或者Ajax异步请求)后,这些事件的回调函数将排在执行队列的最后等待执行(实际上,排队的方式根据浏览器的不同而不同,所以这里只是一个简化);

从第一个JavaScript执行块开始研究,在第一个执行块中两个计时器被初始化:一个10ms的setTimeout()和一个10ms的setInterval()。依据何时何地计时器被初始化(计时器初始化完毕后就会开始计时),计时器实际上会在第一个代码块执行完毕前被触发。但是,计时器上绑定的函数不会立即执行(不被立即执行的原因是JavaScript是单线程的)。实际上,被延迟的函数将依次排在执行队列的最后,等待下一次恰当的时间再执行。

此外,在第一个JavaScript执行块中我们看到了一个“鼠标点击”事件发生了。一个JavaScript回调函数绑定在这个异步事件上了(我们从来不知道用户什么时候执行这个(点击)事件,因此认为它是异步的),这个函数不会被立即执行,和上面的计时器一样,它将排在执行队列的最后,等待下一次恰当的时候执行。

当第一个JavaScript执行块执行完毕后,浏览器会立即问一个问题:哪个函数(语句)在等待被执行?在这时,一个“鼠标点击事件处理函数”和一个“计时器回调函数”都在等待执行。浏览器会选择一个(实际上选择了“鼠标点击事件的处理函数”,因为由图可知它是先进队的)立即执行。而“计时器回调函数”将等待下次适合的时间执行。

注意,当“鼠标点击事件处理函数”执行的时候,setInterval的回调函数第一次被触发了。和setTimeout的回调函数一样,它将排到执行队列的最后等待执行。但是,一定要注意这一点:当setInterval回调函数第二次被触发时(此时setTimeout函数仍在执行)setTimeout的第一次触发将被抛弃掉。当一个很长的代码块在执行时,可能把所有的setInterval回调函数都排在执行队列的后面,代码块执行完之后,结果便会是一大串的setInterval回调函数等待执行,并且这些函数之间没有间隔,直到全部完成。所以,浏览器倾向于的当没有更多interval的处理函数在排队时再将下一个处理函数排到队尾(这是由于间隔的问题)。

我们能够发现,当第三个setInterval回调函数被触发时,之前的setInterval回调函数仍在执行。这就说明了一个很重要的事实:setInterval不会考虑当前正在执行什么,而把所有的堵塞的函数排到队列尾部。这意味着两次setInterval回调函数之间的时间间隔会被牺牲掉(缩减)。

最后,当第二个setInterval回调函数执行完毕后,我们可以看到没有任何程序等待JavaScript引擎执行了。这就意味着浏览器现在在等待一个新的异步事件的发生。在50ms时一个新的setInterval回调函数再次被触发,这时,没有任何的执行块阻塞它的执行了。所以它会立刻被执行。

让我们用一个例子来阐明setTimeout和setInterval之间的区别:
  setTimeout(function(){
    /* Some long block of code... */
    setTimeout(arguments.callee, 10);
  }, 10);
 
  setInterval(function(){
    /* Some long block of code... */
  }, 10);


这两句代码乍一看没什么差别,但是它们是不同的。setTimeout回调函数的执行和上一次执行之间的间隔至少有10ms(可能会更多,但不会少于10ms),而setInterval的回调函数将尝试每隔10ms执行一次,不论上次是否执行完毕。

在这里我们学到了很多知识,总结一下:

    * JavaScript引擎是单线程的,强制所有的异步事件排队等待执行
    * setTimeout 和 setInterval 在执行异步代码的时候有着根本的不同
    * 如果一个计时器被阻塞而不能立即执行,它将延迟执行直到下一次可能执行的时间点才被执行(比期望的时间间隔要长些)
    * 如果setInterval回调函数的执行时间将足够长(比指定的时间间隔长),它们将连续执行并且彼此之间没有时间间隔。

上述这些知识点都是非常重要的。了解了JavaScript引擎是如何工作的,尤其是大量的异步事件(连续)发生时,才能为构建高级应用程序打好基础。

http://www.myext.cn/Article/1177.html
分享到:
评论

相关推荐

    Javascript定时器 一 单线程

    本篇文章将深入探讨JavaScript定时器的工作原理以及它们在单线程环境中的行为。 ### 一、JavaScript的单线程模型 JavaScript是一种解释型的、基于原型的、动态类型的脚本语言,它的执行环境主要是Web浏览器中的...

    Javascript定时器 一 单线程 修正

    JavaScript定时器是编程中不可或...理解JavaScript定时器的工作原理以及单线程环境下的事件循环机制,是编写高效、无阻塞的JavaScript代码的关键。通过合理地运用这些知识,我们可以创建出更流畅、响应更快的Web应用。

    理解javascript定时器中的单线程

    总之,理解JavaScript定时器中的单线程对于JavaScript编程非常重要,它不仅帮助开发者编写出更加高效、性能更加优越的代码,同时还能更好地理解浏览器的工作机制,避免诸如页面假死等问题的出现。

    深入探寻javascript定时器

    javascript单线程 JavaScript的单线程,与它的用途有关。作为浏览器脚本语言,JavaScript的主要用途是与用户互动,以及操作DOM。这决定了它只能是单线程,否则会带来很复杂的同步问题。比如,假定JavaScript同时有两...

    全面的前端开发资料汇总

    JavaScript的单线程性质以及定时器的工作原理 来自John Resig的文章,一次讨论,发现我组长也不理解JavaScript单线程,这也是理解异步回调的基础。 JavaScript严格模式详解 深入理解JavaScript系列 来自博客园的汤姆...

    JavaScript定时器实现的原理分析

    这解释了JavaScript单线程的原理,以及定时器是如何在单线程中异步执行的。 为了弥补单线程的缺陷,引入了异步机制,利用浏览器的多线程。浏览器主要线程包括JavaScript引擎线程、浏览器UI渲染线程、网络请求线程等...

    浅谈Javascript单线程和事件循环.doc

    JavaScript是一种单线程编程语言,这意味着它在任何时刻只能执行一个任务。这一设计背后的考虑是为了保持DOM操作的稳定性。在多线程环境中,对同一元素的并发操作可能导致数据不一致和不可预测的结果,例如上面提到...

    JavaScript单线程和任务队列原理解析

    JavaScript是一种基于单线程执行模型的脚本语言,这一特性源于其主要应用于浏览器环境,用于与用户交互和处理DOM操作。由于多线程操作DOM可能导致复杂的同步问题,JavaScript被设计为单线程,以简化编程逻辑。然而,...

    单线程进度条程序,可以方便的添加到你的工程中

    总之,这个“单线程进度条程序”旨在提供一种简便的方法,将进度条功能集成到您的工程中,通过定时器驱动的更新机制,使得在单线程环境下也能实现平滑的进度显示。虽然它可能不适用于所有场景,但对那些轻量级的、不...

    Javascript定时器 三 setTimeout func 0

    JavaScript使用单线程执行模型,通过事件循环来处理异步操作。事件循环分为多个阶段:宏任务(Macro-task)、微任务(Micro-task)等。在每个完整的一次事件循环中,会先执行一个宏任务,然后处理所有微任务,接着再...

    JavaScript是否可实现多线程 深入理解JavaScript定时机制

    这展示了JavaScript单线程模型的一个重要限制:一旦有一个耗时的操作阻塞了主线程,后续的所有异步操作都会被延迟。 为了提高性能和避免阻塞,现代JavaScript引入了Web Workers,这是一个可以在后台线程中执行的API...

    JavaScript多线程详解

    由于JavaScript的单线程特性,当遇到耗时的操作(如网络请求、定时器或大型计算)时,主线程会被阻塞,直到该任务完成。为了避免阻塞,JavaScript提供了异步编程的方式,如`setTimeout`和`setInterval`。 `...

    js定时器

    值得注意的是,JavaScript是单线程的,这意味着在定时器触发之前,浏览器会先处理完当前执行栈中的所有任务。因此,如果某个任务(如用户交互、网络请求)耗时过长,可能会导致定时器延迟执行,这种情况被称为...

    关于JS定时器(setTimeout setInterval)定时不准问题1

    在浏览器中,所有的JavaScript代码都是在单线程上执行的。因此,异步事件(比如鼠标点击和定时器)仅在线程空闲时才会被调度运行。JavaScript配置了一个任务队列,这些异步事件任务会按照将它们添加到队列的顺序执行...

    定时器源码

    下面,我们将深入探讨定时器的概念、工作原理以及相关知识点。 定时器,简单来说,是一个能够按照预定时间执行特定任务的机制。在编程中,定时器通常通过计数或者等待信号来实现。根据应用场景,定时器可以分为硬件...

    JavaScript中setTimeout的那些事儿

    二、setTimeout与JavaScript单线程模型 JavaScript一直以来被认为是单线程语言,这意味着在同一时间,只能有一个任务在执行。这看似与setTimeout提供的异步执行功能相矛盾。然而,实际上setTimeout并没有打破...

    定时器技术应用2.zip

    本文将深入探讨定时器的工作原理、种类、应用场景以及如何在实际项目中运用定时器技术。 首先,定时器是一种能按照预设的时间间隔执行特定任务的机制。在计算机编程中,定时器通常由操作系统或编程库提供,允许...

    定时器定时器

    但是,由于其单线程模型,可能导致任务间的阻塞。 - `ScheduledExecutorService`:这是Java并发API的一部分,提供了更强大的定时任务管理能力,支持定时和定期任务,并且是多线程的,避免了`Timer`的线程安全问题。...

Global site tag (gtag.js) - Google Analytics