《無所不能的JavaScript編程系列:setTimeout 簡筆》

前言:問題引出

  JavaScript中會常常用到setTimeout來推遲一個函數的執行,如:javascript

1 setTimeout(function(){alert("Hello World");},1000)

  它的意思是會在執行到這句話後延遲1秒鐘(1000毫秒)來彈出alert窗口。java

  那麼再看這一段:
web

1 function a() {
2     setTimeout(function() {alert(1)}, 0);
3     alert(2);
4 }

  注意,這段代碼中的setTimeout延遲設爲了0,就是延遲0毫秒,貌似是不作任何延遲馬上執行。但實際的執行結果確是先彈出2再彈出1,這是爲何呢?JavaScript API文檔明肯定義第二個參數意義爲隔多少毫秒後,回調方法就會被執行。這裏設成0毫秒,理所固然就當即被執行了!?這得從Javascript調用堆棧(call stack)和setTimeout的功能提及。編程

問題剖析

  首先,JavaScript引擎是單線程運行的,瀏覽器不管在何時都有且只有一個線程在運行JavaScript程序,即同一時間只執行一條代碼,因此每個JavaScript代碼執行塊會「阻塞」其它異步事件的執行。瀏覽器

  其次,和其餘的編程語言同樣,Javascript中的函數調用也是經過堆棧實現的。如上例中,在執行函數a的時候,函數a先入棧,若是不給alert(1)加setTimeout,那麼alert(1)第2個入棧,最後是alert(2)。但如今給alert(1)加上setTimeout後,alert(1)就被加入到了一個新的堆棧中等待,並「儘量快」的執行。這個儘量快就是指在a的堆棧完成後就馬上執行,所以實際的執行結果就是先alert(2),再alert(1)。在這裏setTimeout其實是讓alert(1)脫離了當前函數調用堆棧。異步

擴展:AJAX是否真的異步?

  既然說JavaScript是單線程運行的,那麼XMLHttpRequest在鏈接後是否真的異步?async

  其實請求確實是異步的,不過這請求是由瀏覽器新開一個線程請求,當請求的狀態變動時,若是先前已設置回調,這異步線程就產生狀態變動事件放到 JavaScript引擎的處理隊列中等待處理,當任務被處理時,JavaScript引擎始終是單線程運行回調函數,具體點即仍是單線程運行onreadystatechange所設置的函數。編程語言

擴展:setTimeout的 應用場景

1、解決雙擊事件觸發單擊事件的衝突函數

  提示:默認雙擊會先觸發單擊事件,使用延遲單擊事件進行處理。spa

 1 function click(){
 2   isdb=false;
 3   window.setTimeout(cc, 500)
 4   function cc(){
 5     if(isdb!=false)return;
 6     alert("單擊")
 7   }
 8 }
 9 function dblclick(){
10   isdb=true;
11   alert("雙擊")
12 }

2、解決雙擊事件觸發單擊事件的衝突

  AJAX請求後臺,調用webservice或調用大量數據查詢等狀況形成前臺一直處於loading加載框的狀況,可使用setTimeout來解決。

  部分JQ源碼以下:

1 if ( s.async && s.timeout > 0 ) {
2     timeoutTimer = setTimeout(function() {
3         jqXHR.abort("timeout");
4     }, s.timeout );
5 }

編後語

  本博文簡單介紹了setTimeout和JS單線程的知識,這塊水其實很深,但這邊只作一個隨筆。有興趣的同窗,推薦閱讀jQuery做者John的一篇文章:How JavaScript Timers Work,你會對JavaScript單線程本質和setTimeout以及setInterval有更加深入的理解。

  http://ejohn.org/blog/how-javascript-timers-work/ 

  當你理解了JS的單線程和堆棧原理,那在使用JS進行高級程序編寫中,必然會駕輕就熟。

  當你還在說JavaScript是一門玩具型的腳本語言時,它也在遠處嘲笑你對它不夠了解。

相關文章
相關標籤/搜索