從setTimeout談JavaScript運行機制

來源:互聯網
上載者:User

標籤:

前言

  最近在看些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 codesetTimeout(function() {  console.log(‘hello‘);}, 10);// some codedocument.getElementById(‘btn‘).click();// some codesetInterval(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效果呢?

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

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

參考資料
  1. JavaScript 運行機制詳解:再談Event Loop
  2. 深入理解JavaScript定時機制
  3. How JavaScript Timers Work

 

從setTimeout談JavaScript運行機制

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在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.