理解javascript定時器中的單執行緒

理解javascript定時器中的單執行緒

一、JavaScript 引擎是單執行緒的

可以從下面的程式碼中看到,第一個用setTimeout中的程式碼是死迴圈,由於是單執行緒,下面的兩個定時器就沒機會執行了。


<script type="text/javascript">
setTimeout( function(){ while(true){} } , 100); 
setTimeout( function(){ alert('你好!setTimeout'); } , 200); 
setInterval( function(){ alert('你好!setInterval'); } , 200); 
</script>

瀏覽器的核心是多執行緒的,它們在核心制控下相互配合以保持同步,一個瀏覽器至少實現3個常駐執行緒:javascript引擎執行緒,GUI渲染執行緒,瀏覽器事件觸發執行緒。

JavaScript引擎是基於事件驅動單執行緒執行的,JS引擎一直等待著任務佇列中任務的到來然後加以處理,瀏覽器無論再什麼時候都只有一個JS執行緒在執行JS程式。
GUI渲染執行緒負責渲染瀏覽器介面,當介面需要重繪(Repaint)或由於某種操作引發迴流(reflow)時,該執行緒就會執行。但需要注意 GUI渲染執行緒與JS引擎是互斥的,當JS引擎執行時GUI執行緒會被掛起,GUI更新會被儲存在一個佇列中等到JS引擎空閒時立即被執行。
瀏覽器事件觸發執行緒,當一個事件被觸發時該執行緒會把事件新增到待處理佇列的隊尾,等待JS引擎的處理。這些事件可來自JavaScript引擎當前執行的程式碼塊如setTimeOut、也可來自瀏覽器核心的其他執行緒如滑鼠點選、AJAX非同步請求等,但由於JS的單執行緒關係所有這些事件都得排隊等待JS引擎處理。

  由上圖可看出,瀏覽器中的JavaScript引擎是基於事件驅動的,這裡的事件可看作是瀏覽器派給它的各種任務,JavaScript引擎一直等待著任務佇列中任務的到來,由於單執行緒關係,這些任務得進行排隊,一個接著一個被引擎處理。

t1、t2….tn表示不同的時間點,tn下面對應的小方塊代表該時間點的任務。

t1時刻:

1、GUI渲染執行緒
2、瀏覽器事件觸發執行緒:

在t1時間段內,首先是使用者點選了一個滑鼠鍵,點選被瀏覽器事件觸發執行緒捕捉後形成一個滑鼠點選事件,由圖可知,對於JavaScript引擎執行緒來說,這事件是由其它執行緒非同步傳到任務佇列尾的,由於引擎正在處理t1時的任務,這個滑鼠點選事件正在等待處理。
3、定時觸發執行緒:
這裡的瀏覽器模型定時計數器並不是由JavaScript引擎計數的,因為JavaScript引擎是單執行緒的,如果處於阻塞執行緒狀態就計不了時,它必須依賴外部來計時並觸發定時,所以佇列中的定時事件是非同步事件。
4、在這t1的時間段內,繼滑鼠點選事件觸發後,先前已設定的setTimeout定時也到達了,此刻對JavaScript引擎來說,定時觸發執行緒產生了一個非同步定時事件並放到任務佇列中,該事件被排到點選事件回撥之後,等待處理。同理,還是在t1時間段內,接下來某個setInterval定時器也被新增了,由於是間隔定時,在t1段內連續被觸發了兩次,這兩個事件被排到隊尾等待處理。
5、ajax非同步請求:
瀏覽器新開一個http執行緒請求,當請求的狀態變更時,如果先前已設定回撥,這非同步執行緒就產生狀態變更事件放到JavaScript引擎的處理佇列中等待處理。
二、任務的執行順序不同,顯示結果也不同

1)未使用setTimeout函式

在網上找到的一段程式碼例項,這裡用來演示一下。


<a href="#" id="doBtn">do something</a>
<div id="status"></div>
<script type="text/javascript">
var doBtn = document.getElementById('doBtn')
, status = document.getElementById('status');
function sleep(ms) {
var start = new Date();
while (new Date() - start <= ms) {}
}
doBtn.onclick = function(e) {
status.innerHTML = 'doing...please wait...'; 
sleep(3000); // 模擬一個耗時較長的計算過程,3s
status.innerHTML = 'done'; 
return false;
};
</script>

我在firefox中執行了上面的程式碼。計劃是點選“do something”按鈕,然後顯示“doing…please wait…”,接著執行sleep,最後顯示“done”。

但是結果是點選後,瀏覽器卡住3秒左右,最後直接顯示done。

分析下看出,在做status.innerHTML設定的時候,是需要執行GUI渲染執行緒的,但是現在還在執行JavaScript引擎執行緒,而JavaScript引擎執行緒與GUI渲染執行緒是互斥的,所以就最後顯示了done。

 2)使用了setTimeout函式


<a href="#" id="doBtn2">do something timer</a>
<div id="status2"></div>
<script type="text/javascript">
var doBtn2 = document.getElementById('doBtn2')
, status2 = document.getElementById('status2');
function sleep2(ms) {
var start = new Date();
while (new Date() - start <= ms) {}
}
doBtn2.onclick = function(e) {
status2.innerHTML = 'doing...please wait...'; 
setTimeout(function() {
sleep2(3000); 
status2.innerHTML = 'done'; 
}, 100); 
return false;
};
</script>

在“doing…please wait…”後面加了個setTimeout,延時執行,給了瀏覽器渲染的時間,這個時候會顯示出“doing…please wait…”的字樣,然後執行sleep函式,最後顯示“done”。

後面有網友發現在firefox中不起作用,的確有這個問題,後面我修改了一下程式碼,將區域性變數的宣告,onclick的繫結放到了window.onload事件中,等頁面結構載入完成後,我再做指令碼操作。


<script type="text/javascript">
function sleep(ms) {
//...
}
window.onload = function() {
var doBtn = document.getElementById('doBtn'),
status = document.getElementById('status');
var doBtn2 = document.getElementById('doBtn2')
, status2 = document.getElementById('status2');
doBtn.onclick = function(e) {
//...
};
doBtn2.onclick = function(e) {
//...
};
};
</script>

以上就是本文的全部內容,希望對大家的學習有所幫助。

您可能感興趣的文章:

JavaScript 定時器 SetTimeout之定時重新整理視窗和關閉視窗(程式碼超簡單)理解javascript定時器中的setTimeout與setInterval詳解javascript高階定時器javascript中SetInterval與setTimeout的定時器用法淺談Node.js中的定時器深入探尋javascript定時器JS中自定義定時器讓它在某一時刻執行js定時器(執行一次、重複執行)js定時器的使用(例項講解)JavaScript定時器詳解及例項Javascript/Jquery——簡單定時器的多種實現方法獲取焦點時,利用js定時器設定時間執行動作JavaScript定時器實現的原理分析