先觀為敬
//執(zhí)行以下JavaScript代碼,分析代碼執(zhí)行順序
console.log('javascript1')
setTimeout(() => {
console.log('setTimeout1');
Promise.resolve().then(() => {
console.log('promise1');
})
}, 0);
setTimeout(() => {
console.log('setTimeout2');
Promise.resolve().then(() => {
console.log('promise3');
})
setTimeout(() => {
console.log('setTimeout3');
}, 0)
}, 0)
Promise.resolve()
.then(() => {
console.log('promise4');
}).then(() => {
console.log('promise2');
})
console.log('javascript2');
講真的,一開始我是懵逼的,反正正確的打印順序肯定不是從上到下執(zhí)行的。
js瀏覽器的執(zhí)行機制——Event-Loop
這里為什么會強調(diào)說是瀏覽器,我們都知道,在node平臺的JavaScript執(zhí)行機制和瀏覽器端的執(zhí)行是有區(qū)別的,至于何種區(qū)別,不在本文的討論之中
我們知道js語言是單線程的,這是js語言設(shè)計的初衷。起初,作為一個為web開發(fā)的語言,如果設(shè)計為多線程的,那么就會存在一個矛盾。比如:線程A對一個DOM結(jié)構(gòu)刪除了,線程B正在對這個DOM修改,那么瀏覽器是不是就很蛋疼了。但是在瀏覽器執(zhí)行時,又不能按照順序執(zhí)行,如果一個任務(wù)耗時過長,那么后一個任務(wù)也必須等著。那么問題來了,假如我們想打開一個網(wǎng)頁,但是該網(wǎng)頁中包含的大量圖片而且加載很慢,難道我們的網(wǎng)頁要一直卡著直到圖片完全顯示出來?可是問題總歸是要解決的,于是就誕生了:
- 同步執(zhí)行
- 異步執(zhí)行
下面介紹兩個例子:
//同步執(zhí)行
console.log('step1')
console.log('step2')
console.log('step3')
/*
*打印順序為
* step1
* step2
* step3
*/
//異步執(zhí)行
console.log('step1')
setTimeout(function(){
console.log('step2')
},0)
console.log('step3')
/*
*打印順序為
* step1
* step3
* step2
*/
JavaScript執(zhí)行機制Event-Loop如下圖所示

我們嘗試通過JavaScript執(zhí)行機制去分析代碼執(zhí)行順序。首先先介紹兩個概念
- 宏任務(wù):包括整體代碼script,setTimeout,setInterval,setImmediate
- 微任務(wù):Promise,process.nextTick
分析上面的異步例子
console.log('step1')作為第一級宏任務(wù),需要立即執(zhí)行
setTimeout(function(){ console.log('step2') },0)作為第二級宏任務(wù),需要等到第一級宏任務(wù)執(zhí)行完成后,再執(zhí)行
console.log('step3')作為第一級宏任務(wù),需要立即執(zhí)行
再嘗試分析一串簡單的代碼
setTimeout(function(){
console.log('step1')
});
new Promise(function(resolve){
console.log('step2');
for(var i = 0; i < 10000; i++){
i == 99 && resolve();
}
}).then(function(){
console.log('step3')
});
console.log('step4');
1.setTimeout(function(){ console.log('step1') });作為異步任務(wù)放在宏任務(wù)隊列中,等待執(zhí)行
2.new Promise立即執(zhí)行,打印step2,then鏈?zhǔn)俏⑷蝿?wù)放入微任務(wù)隊列中等待本次宏任務(wù)執(zhí)行完成后執(zhí)行
3.console.log('step4')立即執(zhí)行,打印step4
4.本次宏任務(wù)執(zhí)行完成,查看微任務(wù)隊列中發(fā)現(xiàn)還有promise的then鏈沒有執(zhí)行,立即執(zhí)行then鏈中的console.log('step3')
5.本輪任務(wù)完成后,查看宏任務(wù)的隊列里還有一個定時器,執(zhí)行定時器的console.log('step1')
所以打印結(jié)果為step2-step4-step3-step1
當(dāng)理解了js瀏覽器的執(zhí)行機制后,再來看文章開始的例子就可以很清晰的得出代碼打印的結(jié)果
javascript1
javascript2
promise4
promise2
setTimeout1
promise1
setTimeout2
promise3
setTimeout3
setTimeout的執(zhí)行
setTimeout(function(){
console.log('setTimeout')
},1000);
通常setTimeout定時器是指在xx秒后執(zhí)行,其實在js的運行機制里,是在xx秒后將setTimeout里的回調(diào)函數(shù)推入到event queue隊列中,但是并沒有立即執(zhí)行,而是等到j(luò)s主線程空閑之后再執(zhí)行,比如上面的代碼中,一秒鐘后執(zhí)行console.log('setTimeout'),如果主線程的任務(wù)在1秒鐘內(nèi)沒有執(zhí)行完成,那么console.log('setTimeout')也不會在一秒后執(zhí)行。所以說setTimeout在一秒鐘后并且主線程空閑兩個條件同時滿足才會執(zhí)行。