你真的懂JavaScript計時器嗎?
在今天之前我一直以為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)的作用。
相關文章
- 你真的懂JavaScript的正則嗎?JavaScript
- 你真的懂JavaScript基礎型別嗎JavaScript型別
- TCP|你真的懂 HTTP 嗎?TCPHTTP
- 你真的懂函式嗎?函式
- 你真的懂C++嗎?C++
- 你真的懂Python命名嗎?Python
- 騰訊面試,你真的懂HTTP嗎?面試HTTP
- 你真的懂Android的TextView嗎?AndroidTextView
- 分頁查詢,你真的懂嗎?
- 你真的懂 == 和 equals 的區別嗎?
- RabbitMQ的佇列模式你真的懂嗎MQ佇列模式
- JavaScript 你真的瞭解this指向嗎JavaScript
- 你真的已經搞懂JavaScript了嗎?JavaScript
- Spring 系列(三):你真的懂@RequestMapping嗎?SpringAPP
- 你真的懂synchronized鎖?synchronized
- JavaScript基礎——你真的清楚JavaScript是什麼嗎?JavaScript
- 【前端詞典】繼承(一) - 原型鏈你真的懂嗎?前端繼承原型
- 你真的懂模組化嗎?教你CommonJS實現JS
- 你真的懂js獲取可視區寬高嗎JS
- 設計模式你真的懂了嗎?設計模式
- 真的懂Java的String嗎?Java
- 你真的懂Redis的5種基本資料結構嗎?Redis資料結構
- 你真的理解this嗎
- 你真的懂 Java 的記憶體管理和引用型別嗎?Java記憶體型別
- 你真的懂HTML嗎-從”最新快閃記憶體”說起HTML記憶體
- 細說 C# 中的 IEnumerable,你真的懂 foreach 嗎?C#
- 你真的瞭解延時佇列嗎(一)佇列
- 8道經典JavaScript面試題解析,你真的掌握JavaScript了嗎?JavaScript面試題
- 你常說遊戲需要公平,又真的懂遊戲平衡性嗎?遊戲
- 你真的理解setState嗎?
- 你真的理解==和===嗎
- WebView你真的熟悉嗎?WebView
- 你真的知道JS嗎JS
- platform 模組 你真的瞭解你的計算機嗎?Platform計算機
- 瞎搞!你真的懂什麼是ERP、中臺和低程式碼嗎?
- 【UI設計師】你真的瞭解色彩嗎?UI
- JavaScript 計時器JavaScript
- [故事] 瀏覽器快取原理你真的懂嗎?一篇漫畫故事帶你理解透頂!瀏覽器快取