同步和異步任務(wù)
由于JavaScript是一門單線程語言赡磅,是這門語言的核心特征魄缚。
單線程就意味著,所有任務(wù)需要排隊(duì)仆邓,前一個(gè)任務(wù)結(jié)束鲜滩,才會執(zhí)行后一個(gè)任務(wù)伴鳖。如果前一個(gè)任務(wù)耗時(shí)很長,后一個(gè)任務(wù)就不得不一直等著徙硅。為了解決這個(gè)問題榜聂,設(shè)計(jì)者把執(zhí)行的任務(wù)分成兩種:同步任務(wù)、異步任務(wù)
- 同步和異步任務(wù)進(jìn)入到執(zhí)行棧中嗓蘑,分別進(jìn)入不同的執(zhí)行場所须肆,同步的進(jìn)入主線程執(zhí)行,異步的進(jìn)入Event Table并注冊函數(shù)
- 當(dāng)指定的事情完成時(shí)桩皿,Event Table會將這個(gè)函數(shù)移入Event Queue
- 主線程內(nèi)的任務(wù)執(zhí)行完畢為空豌汇,會去Event Queue讀取對應(yīng)的函數(shù),進(jìn)入主線程執(zhí)行泄隔。
- 上述過程會不斷重復(fù)拒贱,也就是常說的Event Loop(事件循環(huán))。
// 首先打印1
console.log(1);
// 進(jìn)入到Event Table佛嬉,注冊了回調(diào)函數(shù)打印2開始執(zhí)行逻澳,2s后進(jìn)入到Event Queue;
setTimeout(() => {
console.log(2);
},2000)
// 也進(jìn)入到Event Table,請求成功后執(zhí)行函數(shù)進(jìn)入到Event Queue
$.ajax({
url:www.javascript.com,
data:data,
success:() => {
console.log('3');
}
})
// 繼續(xù)執(zhí)行主線程中的任務(wù)暖呕,打印3斜做,執(zhí)行完所有的主線程任務(wù)后,在去Event Queue的任務(wù)
console.log(4);
// 1 4 2 3 也可能是1 4 3 2 (根據(jù)ajax請求的時(shí)間是否快于setTimeout)
宏任務(wù)與微任務(wù)
除了廣義的同步任務(wù)和異步任務(wù)湾揽,我們對任務(wù)有更精細(xì)的定義
-
macro-task(宏任務(wù)):
優(yōu)先級:主代碼塊 > setImmediate > MessageChannel > setTimeout / setInterval
-
micro-task(微任務(wù)):
process.nextTick > Promise > MutationObserver
不同類型的任務(wù)會進(jìn)入對應(yīng)的事件隊(duì)列中瓤逼,比如
setTimeout
和setInterval
會進(jìn)入相同的事件隊(duì)列。事件循環(huán)的順序库物,決定js代碼的執(zhí)行順序霸旗。進(jìn)入整體代碼(宏任務(wù))后,開始第一次循環(huán)艳狐。接著執(zhí)行所有的微任務(wù)定硝。然后再次從宏任務(wù)開始,找到其中一個(gè)任務(wù)隊(duì)列執(zhí)行完畢毫目,再執(zhí)行所有的微任務(wù)。
console.log(1);
setTimeout(() => {
console.log(2);
},100)
new Promise(resolve => {
console.log(3);
resolve()
}).then(res => {
console.log(5);
})
console.log(6);
// 1 3 6 5 2
進(jìn)入到整體代碼(宏任務(wù))诲侮,執(zhí)行主線程打印出 1
遇到
setTimeout
镀虐,將其回調(diào)函數(shù)注冊后,放入異步隊(duì)列沟绪,回調(diào)函數(shù)分到到宏任務(wù)執(zhí)行
Promise
刮便,new Promise
立即執(zhí)行,打印3then
函數(shù)分發(fā)到微任務(wù)執(zhí)行打印6
整體代碼script宏任務(wù)執(zhí)行結(jié)束绽慈,檢查到有執(zhí)行的的微任務(wù)恨旱,打印5
第一次event loop完成辈毯,執(zhí)行第2輪的宏任務(wù),打印2
總結(jié)
一個(gè)js的執(zhí)行順序搜贤,可以理解為先分為同步和異步谆沃,再劃分為宏任務(wù)還是微任務(wù)。但這2者是沒有關(guān)系的
setTimeout(() => {
// 宏任務(wù)
new Promise(resolve => {
console.log('1');
resolve();
}).then(() => {
// 微任務(wù)
console.log('2');
});
// 宏任務(wù)
console.log('3');
// 宏任務(wù)
setTimeout(() => {
process.nextTick(function() {
console.log('4');
})
console.log('5');
}, 1000)
}, 1000);
//1s后執(zhí)行順序?yàn)? 3 2 5 4
- 立即執(zhí)行
new Promise
代碼 1仪芒,then
的處理放入微任務(wù) - 執(zhí)行宏任務(wù) 打印 3
- 執(zhí)行宏任務(wù)開啟
setTimeout
唁影,執(zhí)行時(shí)往異步隊(duì)列注冊一個(gè)1s中后執(zhí)行的函數(shù)(可以看上面的異步任務(wù)處理流程) - 宏任務(wù)執(zhí)行完成后,執(zhí)行微任務(wù)打印2
- 主線程內(nèi)的任務(wù)執(zhí)行完畢為空掂名,會去Event Queue讀取對應(yīng)的函數(shù)据沈,放入主線程執(zhí)行
- 在開始新一輪的執(zhí)行先宏任務(wù)也就是打印5
- 再執(zhí)行微任務(wù)
process.nextTick
打印4