JavaScript事件驅動機制&定時器機制
在瀏覽器中,事件作為一個極為重要的機制,給予JavaScript響應使用者操作與DOM變化的能力;在NodeJS中,非同步事件驅動模型則是提高併發能力的基礎。
一、程式如何響應事件
程式響應外部的事件有兩種方式:1. 中斷
作業系統處理鍵盤等硬體輸入就是通過中斷來進行的,這個方式的好處是即使沒有多執行緒,我們也可以放心地執行我們的程式碼,CPU收到中斷訊號之後自動地轉去執行相應的中斷處理程式,處理完成後會恢復原來的程式碼的執行環境繼續執行。這種方式需要硬體的支援,一般來說都會被作業系統封裝起來。
2. 輪詢
迴圈檢測是否有事件發生,如果有就去執行相應的處理程式。這在底層和上層的開發中都有應用。輪詢方式的一個缺點就是:如果在主執行緒的訊息迴圈裡進行耗時操作,程式就無法及時響應新的訊息。
二、JavaScript中定時器功能的特點
無論是Node還是瀏覽器中,都有setTimeout和setInterval這兩個定時器函式,並且其工作特點基本相同。
JavaScript中的定時器並不同於計算機底層的定時中斷。中斷到來時,當前執行程式碼會被打斷,轉去執行定時中斷處理函式。而JavaScript的定時器到時,如果當前執行執行緒沒有正在執行的程式碼,則執行相應的回撥函式;如果當前有程式碼在執行中,JavaScript引擎既不會中斷當前程式碼轉去執行回撥,也不會開新的執行緒執行回撥,而是當前程式碼執行完畢之後才去處理。
console.time("setTimoutLabel"); //標記時間開始
setTimeout(function() {
console.timeEnd("setTimoutLabel"); //標記時間結束
}, 100);
for (var i = 0; i < 100000; i++) { }
執行上面的程式碼,可以看到最終輸出的時間並不是100ms左右,而是數秒。這說明在迴圈完成之前,定時回撥函式確實沒有被執行,而是推遲到了迴圈結束。實際上在JavaScript程式碼執行中,所有的事件都無法得到處理,必須等到當前程式碼全部完成,才能去處理新的事件。這就是為什麼在瀏覽器中運行耗時JavaScript程式碼時,瀏覽器會失去響應。
三、定時器的工作原理
1. javascript引擎只有一個執行緒,迫使非同步事件只能加入佇列去等待執行。
2. 在執行非同步程式碼的時候,如果定時器被正在執行的程式碼阻塞了,它將會進入佇列的尾部去等待執行直到下一次可能執行的時間出現(可能超過設定的延時時間)。setTimeout 和setInterval 是有著本質區別的:setTimeout 這段程式碼會在每次回撥函式執行之後至少需要延時“指定延遲毫秒值”再去執行(可能是更多,但是不會少)。但是setInterval會每隔“指定延遲毫秒值”就去嘗試執行一次回撥函式,不管上一個回撥函式是不是還在執行。