setTimeout(0) 的作用

大家都知道 JavaScript 中的 setTimeout() 可用來延遲執行一段代碼,如:
setTimeout( function() {
  alert("Hello World");
}, 1000) //延時1秒

今天在網上看到了 setTimeout(fn, 0) 的用法,感到有些疑惑,不明白它和直接執行 fn() 有什麼區別,遂去搜集了一下相關的資料,順便分享分享。

先看一段代碼:

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

代碼中的 setTimeout 設爲 0,也就是延遲 0ms,看上去是不做任何延遲立刻執行,即依次彈出 “1”、“2”。但實際的執行結果確是 “2”、“1”。其中的原因得從 setTimeout 的原理說起:

JavaScript 是單線程執行的,也就是無法同時執行多段代碼,當某一段代碼正在執行的時候,所有後續的任務都必須等待,形成一個隊列,一旦當前任務執行完畢,再從隊列中取出下一個任務。這也常被稱爲 “阻塞式執行”。所以一次鼠標點擊,或是計時器到達時間點,或是 Ajax 請求完成觸發了回調函數,這些事件處理程序或回調函數都不會立即運行,而是立即排隊,一旦線程有空閒就執行。假如當前 JavaScript 進程正在執行一段很耗時的代碼,此時發生了一次鼠標點擊,那麼事件處理程序就被阻塞,用戶也無法立即看到反饋,事件處理程序會被放入任務隊列,直到前面的代碼結束以後纔會開始執行。如果代碼中設定了一個 setTimeout,那麼瀏覽器便會在合適的時間,將代碼插入任務隊列,如果這個時間設爲 0,就代表立即插入隊列,但不是立即執行,仍然要等待前面代碼執行完畢。所以 setTimeout 並不能保證執行的時間,是否及時執行取決於 JavaScript 線程是擁擠還是空閒。

至於這樣的寫法有什麼作用,看下面的例子。

<input type="text" οnkeydοwn="show(this.value)">
<div></div>
<script type="text/javascript">
  function show(val) {
    document.getElementsByTagName('div')[0].innerHTML = val;
  }
</script>

這裏綁定了 keydown 事件,意圖是當用戶在文本框裏輸入字符時,將輸入的內容實時地在 <div> 中顯示出來。但是實際效果並非如此,你可以在下面的示例中測試:


可以發現,每按下一個字符時,<div> 中只能顯示出之前的內容,無法得到當前的字符。這時就可以利用 setTimeout(0)

<input type="text" οnkeydοwn="var self=this; setTimeout(function() {show(self.value)}, 0)">
<div></div>
<script type="text/javascript">
  function show(val) {
    document.getElementsByTagName('div')[0].innerHTML = val;
  }
</script>

原因是,當用戶按下按鍵的時候,JavaScript 引擎需要執行 keydown 的事件處理程序,然後更新文本框的 value 值,這兩件事也需要按順序來,事件處理程序執行時,更新 value 值的任務則進入隊列等待。所以我們在 keydown 的事件處理程序裏是無法得到更新後的 value 的,利用 setTimeout,我們把取 value 的操作放入隊列,放在更新 value 值以後,這樣便達到了目的。示例如下,可以發現已經能夠實時顯示輸入的內容。


有人可能會想到利用綁定 keyup 事件來解決,但是 onkeyup 有一個問題,就是當一直按着某個鍵不放時,也會無法得到輸入內容,因爲此時不斷地觸發 keydownkeypress,直到用戶鬆起時纔會觸發 keyup。當然,示例所用的 keydown 也存在一些小缺點,比如用戶如果使用右鍵粘貼,則無法得到粘貼的內容。

所以最理想的方案應該是使用 HTML5 的 input 事件,當文本框或 textareavalue 發生變化時,就會觸發此事件,對粘貼也可以很好地兼容。至於 IE9 之前的瀏覽器,需要使用專有的 onpropertychange 事件
發佈了17 篇原創文章 · 獲贊 8 · 訪問量 12萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章