javascript真的是異步的嗎?且看setTimeout的實現原理以及setTimeout(0)的使用場景


在今天之前我一直以為setTimeout這個函數是異步的,無意中看到了一篇關於setTimeout的文章。發現自己曾經的認識全是錯誤的,趕緊總結下。


先看一段代碼:

var start = new Date();
setTimeout(function(){
    var end = new Date();
    console.log("Time elapsed: ", end - start, "ms");
}, 500);

while (new Date - start <= 1000)
{

}

運行這段腳本能夠看到:Time elapsed的值大概在1001ms左右,肯定會超過1000ms。也就是說:setTimeout失效了,指定的函數並沒有在500ms后運行。而是延遲到1000ms后才運行。


再看一段代碼:

function a()
{
	setTimeout(function(){console.log(1);},0);
	console.log(2);
}
a();

執行這段腳本能夠看到:先打印2后打印1,我們在setTimeout里面指定了0ms,希望能馬上執行,可是實際上沒有效果。


想要理解上面的2段代碼,我們得了解一下javascript中setTimeout的實現原理。首先牢記一點:JavaScript 是單線程運行的,也就是無法同一時候運行多段代碼。

以下這段解釋來自這篇博客

        JavaScript是單線程運行的,無法同一時候運行多段代碼。當某一段代碼正在運行的時候,全部興許的任務都必須等待,形成一個隊列。

一旦當前任務運行完畢,再從隊列中取出下一個任務,這也常被稱為 “堵塞式運行”。所以一次鼠標點擊,或是計時器到達時間點,或是Ajax請求完畢觸發了回調函數。這些事件處理程序或回調函數都不會馬上運行,而是馬上排隊。一旦線程有空暇就運行。假如當前 JavaScript線程正在運行一段非常耗時的代碼,此時發生了一次鼠標點擊。那么事件處理程序就被堵塞。用戶也無法馬上看到反饋。事件處理程序會被放入任務隊列。直到前面的代碼結束以后才會開始運行。假設代碼中設定了一個 setTimeout,那么瀏覽器便會在合適的時間。將代碼插入任務隊列。假設這個時間設為 0,就代表馬上插入隊列,但不是馬上運行。仍然要等待前面代碼運行完成。

所以 setTimeout 並不能保證運行的時間。是否及時運行取決於 JavaScript 線程是擁擠還是空暇。


也就是說setTimeout僅僅能保證在指定的時間過后將任務(須要運行的函數)插入隊列等候,並不保證這個任務在什么時候運行。運行javascript的線程會在空暇的時候,自行從隊列中取出任務然后運行它。javascript通過這樣的隊列機制。給我們制造一個異步運行的假象。

var start = new Date();
setTimeout(function(){
    var end = new Date();
    console.log("Time elapsed: ", end - start, "ms");
}, 500);

console.log("task finished.");
我們之所以會感覺到這段代碼是在異步運行,這是由於javascript線程並沒有由於什么耗時操作而堵塞,所以能夠非常快地取出排隊隊列中的任務然后運行它。

如今我們知道了setTimeout的原理了,如今看下setTimeout(0)的使用場景。以下這個樣例來自這篇文章

<input type="text" onkeydown="show(this.value)">
<div></div>
<script type="text/javascript">
  function show(val) {
    document.getElementsByTagName('div')[0].innerHTML = val;
  }
</script>
這里綁定了 keydown 事件,意圖是當用戶在文本框里輸入字符時。將輸入的內容實時地在 <div> 中顯示出來。可是實際效果並不是如此,能夠發現。每按下一個字符時,<div> 中僅僅能顯示出之前的內容,無法得到當前的字符。


<input type="text" onkeydown="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>

這段代碼使用了setTimeout(0)就能夠實現需要的效果了。

這里事實上涉及2個任務,1個是將鍵盤輸入的字符回寫到輸入框中。一個是獲取文本框的值將其寫入div中。第一個是瀏覽器自身的默認行為。一個是我們自己編寫的代碼。非常顯然。必需要先讓瀏覽器將字符回寫到文本框。然后我們才干獲取其內容寫到div中。改變順序,這這正是setTimeout(0)的作用。


參考文章:setTimeout(0) 的作用



免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM