JS定時器,你所要了解的那點事

Gladyu發表於2018-04-16

背景:

1.資料展示頁加一個自動重新整理功能,讓使用者看到最新資料。 
2.加個定時,過*(時間段)後執行,實現xx效果。
3.調整一下這幾個事件響應的執行順序等等。。。
複製程式碼

開發過程中我們常常要用到定時器去完成某些需求。js為我們準備了兩個函式:setTimeout和setInterval,我們在進入主題之前,先簡單介紹一下它們

1.setTimeout:
var timerId =setTimeout(function(){
    //do    
},delay)

2.setInterval
var timerId2 = setInterval(function() {
    //do
}, delay)
//delay 時間週期
複製程式碼

首先,它們都會返回一個整數編號用來表示定時器。將這個值傳給各自的clear方法可以取消定時。

其次,setTimeout是期望推遲(delay)ms後執行函式, setInterval則是期望間隔(delay)ms就執行一次函式。

為什麼只是說期望那?上一個?瞧瞧

var start_date = 0;
setTimeout(function(){
	var end_date = new Date().getTime()/1000
	console.log('enter setTimeout date : ' +  end_date)
	console.log('time interval : '  + (end_date - start_date) + '秒')
},1000)
function bulky(){
	start_date = new Date().getTime()/1000;
	console.log("bulky start date : " + start_date)
	for(var i=0;i<100000;i++){
		for(var j=0;j<100000;j++){
			var k = 0;
			k = k*k;
		}
	}
}
bulky()
複製程式碼

先預測一下結果再執行程式碼,結果如下:

JS定時器,你所要了解的那點事
跟你想象中的結果一樣嗎?你會發現setTimeout並不是像它期望的那樣----延遲1s就執行,而是要等待bulky()執行結束後才執行,這就是定時器不定時的情形。

定時器是怎麼實現定時的?為什麼會出現不定時的情況?

首先,要明確的一點:javascript是以單執行緒的方式執行的。JavaScript的主要用途是與使用者互動,以及操作DOM。若以多執行緒的方式,則可能出現衝突。假設有兩個執行緒同時操作一個DOM元素,執行緒1要求瀏覽器刪除DOM,而執行緒2卻要求修改DOM樣式,這時瀏覽器就無法決定採用哪個執行緒的操作。當然,我們可以為瀏覽器引入“鎖”的機制來解決這些衝突,但大大提高複雜性,所以 JavaScript從誕生開始就選擇了單執行緒執行。在某一時刻內只能執行特定的一個任務,並且會阻塞其它任務執行。

但是JavaScript 有個基於“Event Loop”併發的模型(不是並行)。前者是邏輯上的同時發生,而後者是物理上的同時發生。所以,單核處理器也能實現併發。

上圖說明一下併發和並行:

JS定時器,你所要了解的那點事

再上圖說明一下Event Loop

JS定時器,你所要了解的那點事

js既然是單執行緒的,也就意味著所有任務需要排隊。所有任務可以分成兩種,一種是同步任務(synchronous),另一種是非同步任務(asynchronous)

  • 同步任務指的是,在主執行緒上排隊執行的任務,只有前一個任務執行完畢,才能執行後一個任務,形成了一個執行棧(execution context stack)

  • 非同步任務指的是,不進入主執行緒,而進入"任務佇列"(task queue)的任務。"任務佇列"是一個事件的佇列(可以當作訊息的佇列來理解)。IO裝置完成一項任務or非同步任務有了執行結果,就在"任務佇列"中新增一個事件,表示相關的操作可以進入"執行棧",就等著執行棧呼叫了。

參照Event Loop的圖,說一下大致的流程----> 執行棧中流式執行函式(同步任務),可能會呼叫API在任務佇列中加入事件(onlick,onload等等)但並不執行。只有當前執行棧沒有其他操作時,任務佇列才會入執行棧中,執行

原理講完了,回到正題。因為setTimeout, setInterval是非同步任務,呼叫之後不會直接進入執行棧,而是進入任務佇列,所以只有等到當前執行棧沒有其他操作,它們才會進入執行棧中執行, 以上就是為什麼定時器不總是定時的原因了。

哦,對了如果delay時間週期設為0,相當於一個插隊操作

舉例

?獻上:

function f1(){
	console.log('f1')
}
function f2(){
	console.log('f2')
}
function f3(){
	console.log('f3')
	setTimeout(function(){
		console.log('setTimeout1')
	},2000)
}
setTimeout(function(){
	console.log('setTimeout2')
},3000)
setTimeout(function(){
	console.log('setTimeout3')
},1000)
setTimeout(function(){
	console.log('setTimeout4')
},0)

f1()
f2()
f3()
複製程式碼

執行結果

JS定時器,你所要了解的那點事
好了,以上內容就是我對js定時器的認識,希望可以幫助到你

未經本人允許,不得轉載。文章有疏漏淺薄之處,請各位大神斧正

相關文章