setTimeout運行原理
先觀察以下代碼
var start = new Date;
setTimeout(function(){
console.log('時間流逝了:'+(new Date - start)+'毫秒');
}, 200);
while (new Date - start < 1000) {}
console.log(1);
function doSoming(){
setTimeout(function(){
console.log('時間又流逝了:'+(new Date - start)+'毫秒');
},10);
}
doSoming();
while (new Date - start < 2000) {}
console.log(2);
上述代碼的結(jié)果是:
以上結(jié)果表明幾個現(xiàn)象:
1.定時任務(wù)在非定時任務(wù)后執(zhí)行丁逝;
2.setTimeout中的delay參數(shù)不一定越小越早執(zhí)行;
分析第一點看出來圈浇,很明顯卜壕,settimeout是在其他非延時任務(wù)后執(zhí)行的您旁,簡單的原理就是:
在現(xiàn)有瀏覽器環(huán)境中,Javascript執(zhí)行引擎是單線程的轴捎,主線程的語句和方法鹤盒,會阻塞定時任務(wù)的運行蚕脏,在Javascript執(zhí)行引擎之外,存在一個任務(wù)隊列侦锯,當(dāng)在代碼中調(diào)用setTimeout()方法時驼鞭,注冊的延時方法會掛到瀏覽器內(nèi)核其他模塊處理,當(dāng)延時方法到達觸發(fā)條件尺碰,即到達設(shè)置的延時時間時挣棕,該模塊再將要執(zhí)行的方法添加至該模塊的任務(wù)隊列中。這一過程與執(zhí)行引擎主線程獨立亲桥,執(zhí)行引擎在主線程方法執(zhí)行完畢洛心,到達空閑狀態(tài)時,才會從該模塊的任務(wù)隊列中順序提取任務(wù)來執(zhí)行题篷,這期間的時間词身,可能大于注冊任務(wù)時設(shè)置的延時時間;
瀏覽器在空閑狀態(tài)下番枚,會不斷的嘗試從模塊的任務(wù)隊列中提取任務(wù)法严,這稱為事件循環(huán)模型;
分析第二點葫笼,原因是什么呢深啤?
因為while函數(shù)內(nèi)部的非定時任務(wù)阻塞了約1000ms,第一個定時任務(wù)已經(jīng)在約200ms時被放進了任務(wù)隊列中路星,此時第二個定時任務(wù)還沒有運行到墓塌。如果while函數(shù)阻塞的時間+第二個定時任務(wù)delay的時間,那么結(jié)果就會變成
setTimeout循環(huán)內(nèi)的閉包陷阱
for(var i =0; i <10; i++){
setTimeout(function(){
console.log(i);
},1000);
}
上述代碼的輸出結(jié)果是什么奥额?
輸出十次10,這是因為匿名函數(shù)閉包的作用访诱,在循環(huán)結(jié)束后再執(zhí)行定時任務(wù)時垫挨,i已經(jīng)遞增到10了,原來的值被改變了触菜。
為了得到理想的結(jié)果九榔,有以下解決方案
- 自執(zhí)行匿名函數(shù)
每次循環(huán)時,使用自執(zhí)行匿名函數(shù)進行對i的拷貝涡相,再執(zhí)行定時任務(wù)時就會訪問當(dāng)時傳入的參數(shù)哲泊。
for(var i =0; i <10; i++){
(function(e){
setTimeout(function(){
console.log(e);
},1000);
})(i);
}
- 定時器內(nèi)自執(zhí)行函數(shù)
for(var i =0; i <10; i++){
setTimeout((function(e){
console.log(e);
})(i),1000)
}
- es6 let
使用let關(guān)鍵字使得for循環(huán)變成塊級作用域
for(let i =0; i <10; i++){
setTimeout(function(){
console.log(i);
},1000);
}
參考鏈接: 你真的知道setTimeout是如何運行的嗎