下面代碼的輸出結(jié)果:
for(var i = 0; i < 10; i++) {
setTimeout(function() {
console.log(i);
}, 1000);
}
答案是:10個10
由此梦谜,我們來了解一下JavaScript的定時機制仔蝌。
眾所周知,JavaScript引擎是單線程的一喘,我們先來看一下JavaScript的線程圖示:
如圖,一般瀏覽器內(nèi)核中至少常駐三個線程:JavaScript引擎線程嗜暴、GUI渲染線程凸克、瀏覽器事件觸發(fā)線程等。
其他線程產(chǎn)生的任務(wù)放到JavaScript引擎線程的隊列中闷沥,然后一個接一個的執(zhí)行萎战。
//代碼一
setTimeout(function() {
console.log("123");
}, 1000);
//代碼二
setTimeout(function() {
console.log("abc");
setTimeout(arguments.callee, 1000);
}, 1000);
//代碼三
setInterval(function(){
console.log("xyz");
}, 1000);
代碼一,延遲執(zhí)行狐赡,一秒后控制臺打印出123
代碼三撞鹉,循環(huán)執(zhí)行疟丙,每過一秒(<=1s)打印xyz
代碼二,也是循環(huán)執(zhí)行鸟雏,每過一秒(>=1s)打印abc
由于arguments.callee不推薦使用了享郊,所以代碼二可以等價的寫成:
setTimeout(function a() {
console.log("abc");
setTimeout(a, 1000);
}, 1000);
代碼二和代碼三是循環(huán)執(zhí)行,會一直執(zhí)行下去孝鹊,容易造成瀏覽器假死炊琉,沒反應(yīng),如果想要結(jié)束循環(huán)又活,可以通過如下方式:
//代碼二改成這樣苔咪,只執(zhí)行10次
var num=0;
setTimeout(function a() {
num++;
console.log("abc");
if(num<10){
setTimeout(a, 1000);
}
}, 1000);
//代碼二也可以改成這樣,執(zhí)行11次
var num=0;
setTimeout(function a() {
num++;
console.log("abc");
var timer=setTimeout(a, 1000);
if(num>10){
clearTimeout(timer);
}
}, 1000);
同樣的道理柳骄,代碼三可以改成如下寫法:
//9次
var num=0;
setInterval(function(){
num++;
if(num<10){
console.log("xyz");
}
}, 1000);
//11次
var num=0;
var i=setInterval(function(){
num++;
console.log("xyz");
if(num>10){
clearInterval(i);
}
}, 1000);
差不多就這樣吧团赏,總結(jié)一下:
- JavaScript是單線程的
- JavaScript引擎是基于事件驅(qū)動的
- 定時器產(chǎn)生的異步事件會插入到JavaScript引擎的事件隊列中
- JavaScript引擎執(zhí)行完同步事件后會去繼續(xù)執(zhí)行事件隊列中的異步事件,這就是為什么如下代碼先打印0再打印1的原因
setTimeout(function(){
console.log(1);
},0);
console.log(0);
- 兩個setTimeout構(gòu)成的循環(huán)可以使用clearTimeout()去結(jié)束循環(huán)
- setInterval循環(huán)可以使用clearInterval()去結(jié)束循環(huán)
參考閱讀:
深入理解JavaScript定時機制
深入理解定時器系列第一篇——理解setTimeout和setInterval
瀏覽器UI多線程及對JavaScript單線程底層運行機制的理解