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)脫離了當前函數調用堆棧。異步
既然說JavaScript是單線程運行的,那麼XMLHttpRequest在鏈接後是否真的異步?async
其實請求確實是異步的,不過這請求是由瀏覽器新開一個線程請求,當請求的狀態變動時,若是先前已設置回調,這異步線程就產生狀態變動事件放到 JavaScript引擎的處理隊列中等待處理,當任務被處理時,JavaScript引擎始終是單線程運行回調函數,具體點即仍是單線程運行onreadystatechange所設置的函數。編程語言
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是一門玩具型的腳本語言時,它也在遠處嘲笑你對它不夠了解。