How JavaScript Timers Work [Copy]

來源:互聯網
上載者:User

轉載這篇文章的原因是它解釋了setTimeout和setInterval之間的區別,對於這篇文章,部落格園有位朋友進行了翻譯,網址如下:http://www.cnblogs.com/rainman/archive/2008/12/26/1363321.html

翻譯內容為:

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回呼函數再次被觸發,這時,沒有任何的執行塊阻塞它的執行了。所以它會立刻被執行。

讓我們用一個例子來闡明setTimeoutsetInterval之間的區別:

  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引擎是單線程的,強制所有的非同步事件排隊等待執行
  • setTimeoutsetInterval在執行非同步代碼的時候有著根本的不同
  • 如果一個計時器被阻塞而不能立即執行,它將順延強制直到下一次可能執行的時間點才被執行(比期望的時間間隔要長些)
  • 如果setInterval回呼函數的執行時間將足夠長(比指定的時間間隔長),它們將連續執行並且彼此之間沒有時間間隔。

上述這些知識點都是非常重要的。瞭解了JavaScript引擎是如何工作的,尤其是大量的非同步事件(連續)發生時,才能為構建進階應用程式程式打好基礎。

英文原文如下:

At a fundamental level it's important to understand how JavaScript timers work. Often times they behave unintuitively because of the single thread which they are in. Let's start by examining the three functions to which we have access that can construct and manipulate timers.

  • var id = setTimeout(fn, delay);- Initiates a single timer which will call the specified function after the delay. The function returns a unique ID with which the timer can be canceled at a later time.
  • var id = setInterval(fn, delay);- Similar tosetTimeoutbut continually calls the function (with a delay every time) until it is canceled.
  • clearInterval(id);,clearTimeout(id);- Accepts a timer ID (returned by either of the aforementioned functions) and stops the timer callback from occurring.

In order to understand how the timers work internally there's one important concept that needs to be explored: timer delay is not guaranteed. Since all JavaScript in a browser executes on a single thread asynchronous events (such as mouse clicks and timers) are only run when there's been an opening in the execution. This is best demonstrated with a diagram, like in the following:

尺寸: 500 x 375
瀏覽: 613 次
點擊開啟新視窗瀏覽全圖" border="0">

There's a lot of information in this figure to digest but understanding it completely will give you a better realization of how asynchronous JavaScript execution works. This diagram is one dimensional: vertically we have the (wall clock) time, in milliseconds. The blue boxes represent portions of JavaScript being executed. For example the first block of JavaScript executes for approximately 18ms, the mouse click block for approximately 11ms, and so on.

Since JavaScript can only ever execute one piece of code at a time (due to its single-threaded nature) each of these blocks of code are "blocking" the progress of other asynchronous events. This means that when an asynchronous event occurs (like a mouse click, a timer firing, or an XMLHttpRequest completing) it gets queued up to be executed later (how this queueing actually occurs surely varies from browser-to-browser, so consider this to be a simplification).

To start with, within the first block of JavaScript, two timers are initiated: a 10mssetTimeoutand a 10mssetInterval. Due to where and when the timer was started it actually fires before we actually complete the first block of code. Note, however, that it does not execute immediately (it is incapable of doing that, because of the threading). Instead that delayed function is queued in order to be executed at the next available moment.

Additionally, within this first JavaScript block we see a mouse click occur. The JavaScript callbacks associated with this asynchronous event (we never know when a user may perform an action, thus it's consider to be asynchronous) are unable to be executed immediately thus, like the initial timer, it is queued to be executed later.

After the initial block of JavaScript finishes executing the browser immediately asks the question: What is waiting to be executed? In this case both a mouse click handler and a timer callback are waiting. The browser then picks one (the mouse click callback) and executes it immediately. The timer will wait until the next possible time, in order to execute.

Note that while mouse click handler is executing the first interval callback executes. As with the timer its handler is queued for later execution. However, note that when the interval is fired again (when the timer handler is executing) this time that handler execution is dropped. If you were to queue up all interval callbacks when a large block of code is executing the result would be a bunch of intervals executing with no delay between them, upon completion. Instead browsers tend to simply wait until no more interval handlers are queued (for the interval in question) before queuing more.

We can, in fact, see that this is the case when a third interval callback fires while the interval, itself, is executing. This shows us an important fact: Intervals don't care about what is currently executing, they will queue indiscriminately, even if it means that the time between callbacks will be sacrificed.

Finally, after the second interval callback is finished executing, we can see that there's nothing left for the JavaScript engine to execute. This means that the browser now waits for a new asynchronous event to occur. We get this at the 50ms mark when the interval fires again. This time, however, there is nothing blocking its execution, so it fires immediately.

Let's take a look at an example to better illustrate the differences betweensetTimeoutandsetInterval.

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

These two pieces of code may appear to be functionally equivalent, at first glance, but they are not. Notably thesetTimeoutcode will always have at least a 10ms delay after the previous callback execution (it may end up being more, but never less) whereas thesetIntervalwill attempt to execute a callback every 10ms regardless of when the last callback was executed.

There's a lot that we've learned here, let's recap:

  • JavaScript engines only have a single thread, forcing asynchronous events to queue waiting for execution.
  • setTimeoutandsetIntervalare fundamentally different in how they execute asynchronous code.
  • If a timer is blocked from immediately executing it will be delayed until the next possible point of execution (which will be longer than the desired delay).
  • Intervals may execute back-to-back with no delay if they take long enough to execute (longer than the specified delay).

All of this is incredibly important knowledge to build off of. Knowing how a JavaScript engine works, especially with the large number of asynchronous events that typically occur, makes for a great foundation when building an advanced piece of application code.

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.