1. 程式人生 > >從setTimeout談JavaScript執行機制

從setTimeout談JavaScript執行機制

從setTimeout說起

  眾所周知,JavaScript是單執行緒的程式設計,什麼是單執行緒,就是說同一時間JavaScript只能執行一段程式碼,如果這段程式碼要執行很長時間,那麼之後的程式碼只能盡情地等待它執行完才能有機會執行,不像人一樣,人是多執行緒的,所以你可以一邊觀看某島國動作片,一邊盡情揮灑汗水。JavaScript單執行緒機制也是迫不得已,假設有多個執行緒,同時修改某個dom元素,那麼到底是聽哪個執行緒的呢?

  既然已經明確JavaScript是單執行緒的語言,於是我們想方設法要想出JavaScript的非同步方案也就可以理解了。比如執行到某段程式碼,需求是1000ms後呼叫方法A,JavaScript沒有sleep函式能掛起執行緒一秒啊?如何能夠使得程式碼做到一邊等待A方法執行,一邊繼續執行下面的程式碼,彷彿開了兩個執行緒一般?機制的科學家們想出了setTimeout方法。

  setTimeout方法想必大家都已經很熟悉了,那麼setTimeout(function(){..}, a)真的是ams後執行對應的回撥嗎?

setTimeout(function() {
  console.log('hello world');
}, 1000);

while(true) {};

  1s中之後,控制檯並沒有像預料中的一樣輸出字串,而網頁標籤上的圈圈一直轉啊轉,掐指一算,可能陷入while(true){}的死迴圈中了,可是為什麼呢?雖然會陷入死迴圈可是也得先輸出字串啊!這就要扯到JavaScript執行機制了。

JavaScript執行機制

  一段JavaScript程式碼到底是如何執行的?阮一峰老師有篇不錯的文章(

JavaScript 執行機制詳解:再談Event Loop),我就不再重複造輪子了;如果覺得太長不看的話,樓主簡短地大白話描述下。一段js程式碼(裡面可能包含一些setTimeout、滑鼠點選、ajax等事件),從上到下開始執行,遇到setTimeout、滑鼠點選等事件,非同步執行它們,此時並不會影響程式碼主體繼續往下執行(當執行緒中沒有執行任何同步程式碼的前提下才會執行非同步程式碼),一旦非同步事件執行完,回撥函式返回,將它們按次序加到執行佇列中,這時要注意了,如果主體程式碼沒有執行完的話,是永遠也不會觸發callback的,這也就是上面的一段程式碼導致瀏覽器假死的原因(主體程式碼中的while(true){}還沒執行完)。

  網上還有一篇流傳甚廣的文章(猛戳How JavaScript Timers Work),文章裡有張很好的圖,我把它盜過來了。

  文章裡沒有針對這幅圖的程式碼,為了能更好的說明流程,我嘗試著給出程式碼:

// some code

setTimeout(function() {
  console.log('hello');
}, 10);

// some code

document.getElementById('btn').click();

// some code

setInterval(function() {
  console.log('world');
}, 10);

// some code

   我們開始執行程式碼。第一塊程式碼大概執行了18ms,也就是JavaScript的主體程式碼,在執行過程中,先觸發了一個setTimeout函式,程式碼繼續執行,只等10ms後響應setTimeout的回撥,接著是一個滑鼠點選事件,該事件有個回撥(或許是alert一些東西),不能立即執行(單執行緒),因為js主體程式碼還沒執行完,所以這個回撥被插入執行佇列中,等待執行;接著setInterval函式被執行,我們知道,此後每隔10ms都會有回撥(嘗試)插入佇列中,執行到第10ms的時候,setTimeout函式的回撥插入佇列。js函式主體執行完後,大概是18ms這個點,我們發現佇列中有個click的callback,還有個setTimeout的callback,於是我們先執行前者,在執行的過程中,setInterval的10ms響應時間也過了,同樣回撥被插入佇列。click的回撥執行完,執行setTimeout的回撥,這時又10ms過去了,setInterval又產生了回撥,但是這個回撥被拋棄了,之後發生的事大家都一目瞭然了。

  這裡有一點我不太明白,就是關於interval回撥的drop。按照How JavaScript Timers Work裡的說法是,如果等待佇列裡已經有同一個interval函式的回調了,將不會有相同的回撥插入等待佇列。

“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.”

  查到一篇前輩的文章Javascript定時器學習筆記,裡面說“為了確保定時器程式碼插入到佇列總的最小間隔為指定時間。當使用setInterval()時,僅當沒有該定時器的任何其他程式碼例項時,才能將定時器程式碼新增到程式碼佇列中”。但是我自己實踐了下覺得可能並非如此:

var startTime = +new Date;
var count = 0;
var handle = setInterval(function() {
  console.log('hello world');
  count++;
  if(count === 1000) clearInterval(handle);
}, 10);

while(+new Date - startTime < 10 * 1000) {};

  按照上文的說法,由於while對執行緒的“阻塞”,使得相同的setInterval的回撥不能加在等待佇列中,但是實際在chrome和ff的控制檯都輸出了1000個hello world的字串,我也去原文博主的文章下留言詢問了下,暫時還沒答覆我;也可能是我對setInterval的認識的姿勢不對導致,如果有知道的朋友還望不吝賜教,萬分感激!

  總之,定時器僅僅是在未來的某個時刻將程式碼新增到程式碼佇列中,執行時機是不能保證的。

setTimeout VS setInterval

  以前看到過這樣的話,setInterval的功能都能用setTimeout去實現,想想也對,無窮盡地遞迴呼叫setTimeout不就是setInterval了嗎?

setInterval(function() {
  // some code
}, 10);

  根據前文描述,我們大概懂了以上setInterval回撥函式的執行時間差<=10ms,因為可能會由於執行緒阻塞,使得一系列的回撥全部在排隊。用setTimeout實現的setInterval效果呢?

// 1
function func() {
  setTimeout(function() {
    // some code
    func();
  }, 10);
}

func();


// 2
setTimeout(function() {
  // some code
  setTimeout(arguments.callee, 1000);
}, 10);

  很顯然兩個回撥之間的間隔是>10ms的,因為前面一個回撥在佇列中排隊,如果沒有等到,是不會執行下面的回撥的,而>10ms是因為回撥中的程式碼也要執行時間。換句話說,setInterval的回撥是並列的,前一個回撥(有沒有執行)並不會影響後一個回撥(插入佇列),而setTimeout之間的回撥是巢狀的,後一個回撥是前一個回撥的回撥(有點繞口令的意思)

參考資料

2015.7.1修正

  經驗證,確實是樓主對於setInterval認識的姿勢有誤,也對得起兩個反對的差評,當使用setInterval()時,僅當沒有該定時器的任何其他程式碼例項時,才能將定時器程式碼新增到程式碼佇列中。

  樓主的示例程式碼,正如評論中說的一樣,無論有無阻塞,都會執行1000次。程式碼修改如下:

var startTime = +new Date;

var handle = setInterval(function() {
  console.log('hello world');
}, 3000);

while(+new Date - startTime < 10 * 1000) {};

  如果按照之前的認識,在while阻塞過程中,setInterval應該插入了3個回撥函式,而當while執行完後,控制檯應該打出連續3個字串,但是並沒有,說明確實只加入了一個回撥函式,其他兩個被drop了。而舉了個更好的例子,詳見Javascript定時器學習筆記評論部分的第二個示例程式碼。

  所以確實在使用setInterval時:

  1. 某些間隔會被跳過
  2. 多個定時器的程式碼執行之間的間隔可能會比預期的小(當前的setInterval回撥正在執行,後一個新增)

相關推薦

setTimeoutJavaScript執行機制

從setTimeout說起   眾所周知,JavaScript是單執行緒的程式設計,什麼是單執行緒,就是說同一時間JavaScript只能執行一段程式碼,如果這段程式碼要執行很長時間,那麼之後的程式碼只能盡情地等待它執行完才能有機會執行,不像人一樣,人是多執行緒的,所以你可以一邊觀看某島國動作片,一邊盡情揮

理解JavaScript 執行機制及非同步回撥(setTimeout/setInterval/Promise)

對於javascript執行機制的理解一直都是混淆不清。在面試或工作的過程中,也經常會遇到程式碼執行順序或函式生命週期載入等類似的問題,這些多多少少都與javascript的執行機制相關。今天發現一篇很好的文章,欣喜之餘,加以轉載,供感興趣的小夥伴學習,感謝作者分享。 不論

我看樸靈評註阮一峰的《JavaScript 執行機制詳解:再Event Loop》

阮一峰和樸靈對我來說都是大牛,他們倆的書我都買過,阮老師的譯作《軟體隨想錄》和樸靈的《深入淺出node.js》。這個事情已經過了4個月了,所以我拿來講應該也沒啥問題。 這件事情是這樣的,阮一峰在自己的部落格寫了篇文章《JavaScript 執行機制詳解:再談Event Lo

徹底弄懂 JavaScript 執行機制

函數 大名 定時 意思 技術 渲染 文字 根據 java 本文的目的就是要保證你徹底弄懂javascript的執行機制,如果讀完本文還不懂,可以揍我。 不論你是javascript新手還是老鳥,不論是面試求職,還是日常開發工作,我們經常會遇到這樣的情況:給定的幾行

JavaScript執行機制

指定 i++ 立即執行 使用 func bubuko 異步任務。 下一個 href 原文   簡書原文:https://www.jianshu.com/p/0d2d42fbe1dc 大綱   1、場景分析  2、執行機制相關知識點  3、以實例來說明JavaScript的執

JavaScript 執行機制:Event Loop

JavaScript 是單執行緒語言。單執行緒就意味著需要排隊,前一個任務完成才能執行下一個任務。所以任務分為兩種,同步任務和非同步任務。 同步任務指的是,在主執行緒上排隊執行的任務,只有前一個任務執行完畢,才能執行下一個任務。 非同步任務指的是不進入主執行緒,而進入“任務

這一次,徹底弄懂 JavaScript 執行機制

本文的目的就是要保證你徹底弄懂javascript的執行機制,如果讀完本文還不懂,可以揍我。 文章轉自:https://juejin.im/post/59e85eebf265da430d571f89 不論你是javascript新手還是老鳥,不論是面試求職,還是日常開發

JavaScript 執行機制 巨集任務微任務

1.關於javascript javascript是一門單執行緒語言,在最新的HTML5中提出了Web-Worker,但javascript是單執行緒這一核心仍未改變。所以一切javascript版的"多執行緒"都是用單執行緒模擬出來的,一切javascrip

傻傻分不清的javascript執行機制

學習到javascript的執行機制時,有幾個概念經常出現在各種文章中且容易混淆。Execution Context(執行環境或執行上下文),Context Stack (執行棧),Variable Object(VO: 變數物件),Active Object(AO: 活動物件),LexicalEnvi

js執行機制

關於js執行機制,老早之前就一直想寫篇文章做個總結,因為和js執行順序的面試題碰到的特別多,每次碰到總是會去網上查,沒有系統地總結,搞得每次碰到都是似懂非懂的感覺,這篇文章就係統的總結一下js執行機制。 任務佇列 大家都知道js最大的特點就是單執行緒執行,這就是為什麼js簡單易學的一個重要原

JavaScript執行機制(堆、棧、訊息佇列)

棧 JavaScript是單執行緒語言,主執行緒執行同步程式碼。 函式呼叫時, 便會在記憶體形成了一個“呼叫記錄”, 又稱“呼叫幀”, 儲存呼叫位置和內部變數等資訊。 如果函式內部還呼叫了其他函式,那麼在呼叫記錄上方又會形成一個呼叫記錄, 所有的呼叫

這一次,徹底弄懂 JavaScript 執行機制(寫的非常好)

本文的目的就是要保證你徹底弄懂javascript的執行機制,如果讀完本文還不懂,可以揍我。 不論你是javascript新手還是老鳥,不論是面試求職,還是日常開發工作,我們經常會遇到這樣的情況:給定的幾行程式碼,我們需要知道其輸出內容和順序。因為javasc

徹底弄懂JavaScript 執行機制

很多人在面試的時候都會碰到這麼一道面試題:給一段程式碼,寫出執行結果和順序。其中側重的知識點可能也不盡相同。寫這篇文章,主要是把其中可能涉及到的知識點都簡單說一下,自己也好好梳理一下。如果文章有說的不對的地方,儘管diss。 瀏覽器核心分為渲染引擎和JS引擎,不過由於J

JavaScript 執行機制

本文的目的就是要保證你徹底弄懂javascript的執行機制,如果讀完本文還不懂,可以揍我。 不論你是javascript新手還是老鳥,不論是面試求職,還是日常開發工作,我們經常會遇到這樣的情況:給定的幾行程式碼,我們需要知道其輸出內容和順序。因為javascript是一門單執行緒語言,所以我們可以得出結論

【js】javaScript 執行機制

dia 完整 web ack 指定 tor mon 圖片加載 www. javascript 是一門單線程語言(按照語句一行一行的執行) let a = ‘1‘; console.log(a); let b = ‘2‘; console.log(b); 這樣

原生js深入理解系列(七)--- 讀JavaScript 執行機制的一點小總結

開發十年,就只剩下這套架構體系了! >>>   

JavaScript 執行機制以及Event Loop(事件迴圈)

一、JavaScript單執行緒 眾所周知JavaScript是一門單執行緒語言,也就是說,在同一時間內JS只能做一件事。為什麼JavaScript不能有多個執行緒呢?這樣不是能夠提高效率嗎? JavaScript的單執行緒,與它的用途有關。作為瀏覽器指令碼語言,JavaScript的主要用途是與使用者互

【THE LAST TIME】徹底吃透 JavaScript 執行機制

前言 The last time, I have learned 【THE LAST TIME】一直是我想寫的一個系列,旨在厚積薄發,重溫前端。 也是給自己的查缺補漏和技術分享。 歡迎大家多多評論指點吐槽。 系列文章均首發於公眾號【全棧前端精選】,筆者文章集合詳見Nealyang/personalB

Event LoopJS的執行機制

這裡主要是結合Event Loop來談JS程式碼是如何執行的。   讀這部分的前提是已經知道了JS引擎是單執行緒,而且這裡會用到前面說的的幾個概念:(如果不是很理解,可以回頭溫習) JS引擎執行緒 事件觸發執行緒【輪訓】 定時觸發器執行緒 然後再理解一個概念:

JavaScript——setTimeout()的執行了解js的單執行緒和非同步

眾所周知,JavaScript是單執行緒的,那麼到底什麼是單執行緒呢?今天我們就用setTimeout()舉例,看看單執行緒到底是什麼樣的。 單執行緒,從名字就能知道,它只有一個主執行緒。單執行緒就意味著,所有任務需要排隊,前一個任務結束,才會執行後一個任務。如果前一個任務