JavaScript是單線程執(zhí)行的,即 js 中任務(wù)是按順序依次執(zhí)行的,但若其中一個(gè)任務(wù)執(zhí)行時(shí)間過(guò)長(zhǎng),后續(xù)任務(wù)會(huì)一直等待,造成程序假死。 為了解決這個(gè)問(wèn)題,將任務(wù)分為同步任務(wù)和異步任務(wù),其中異步任務(wù)又分為宏任務(wù)和微任務(wù)。
同步任務(wù)與異步任務(wù):
同步任務(wù):又叫做非耗時(shí)任務(wù),指的是在主線程上排隊(duì)執(zhí)行的那些任務(wù)
? ?只有前一個(gè)任務(wù)執(zhí)行完畢,才能執(zhí)行后一個(gè)任務(wù)
異步任務(wù):又叫做耗時(shí)任務(wù),異步任務(wù)由JavaScript 委托給宿主環(huán)境進(jìn)行執(zhí)行?
當(dāng)異步任務(wù)執(zhí)行完成后,會(huì)通知JavaScript 主線程執(zhí)行異步任務(wù)的回調(diào)函數(shù)
? ?????????????????1. 同步任務(wù)由JavaScript主線程次序執(zhí)行
????????????????????2. 異步任務(wù)委托給宿主環(huán)境執(zhí)行
? ? ? ? ? ? ? ? ? ? 3. 已完成的異步任務(wù)對(duì)應(yīng)的回調(diào)函數(shù),會(huì)被加入到任務(wù)隊(duì)列中等待執(zhí)行
????????????????????4. JavaScript 主線程執(zhí)行棧被清空后會(huì)讀取任務(wù)隊(duì)列中的回調(diào)函數(shù),次序執(zhí)行
? ??????????????????5. JavaScript 主線程不斷重復(fù)上面的第4步
JavaScript主線程從“任務(wù)隊(duì)列”中讀取異步任務(wù)的回調(diào)函數(shù),放到執(zhí)行棧中依次執(zhí)行。這個(gè)過(guò)程是循環(huán)不斷的,所以整個(gè)的這種運(yùn)行機(jī)制又稱為 EventLoop (事件循環(huán))
宏任務(wù)與微任務(wù)
宏任務(wù):異步Ajax請(qǐng)求,setTimeout,setInterval,文件操作,new Promise等
微任務(wù):Promise.then、.catch、.finally,process.nextTick等
宏任務(wù)與微任務(wù)是交替執(zhí)行的,每次執(zhí)行完宏任務(wù)都會(huì)檢查是否有微任務(wù)
代碼示例:
```
console.log('A');
setTimeout(function() {
? console.log('B');
}, 0);
Promise.resolve().then(function() {
? console.log('C');
}).then(function() {
? console.log('D');
});
console.log('E');
```
先執(zhí)行同步任務(wù)打印A和E,再執(zhí)行異步任務(wù)中的微任務(wù),打印C和D,最后執(zhí)行宏任務(wù)打印B
最終打印結(jié)果:AECDB
可能有人會(huì)問(wèn),為什么微任務(wù)優(yōu)先于宏任務(wù)執(zhí)行,其實(shí)并不是,這里先執(zhí)行微任務(wù)的原因是,script本身也是一個(gè)宏任務(wù),這個(gè)宏任務(wù)執(zhí)行結(jié)果就是添加各種微任務(wù)與宏任務(wù),比如下面代碼中,同步任務(wù)執(zhí)行完成后,會(huì)先執(zhí)行script的宏任務(wù),即添加一個(gè)setTimeout的宏任務(wù)與一個(gè)Promise.then的微任務(wù),這個(gè)宏任務(wù)執(zhí)行完成后,就該執(zhí)行Promise.then的微任務(wù)了。并不是微任務(wù)優(yōu)先級(jí)大于宏任務(wù),而是這個(gè)宏任務(wù)執(zhí)行感知不強(qiáng),會(huì)讓人感覺(jué)并沒(méi)有執(zhí)行宏任務(wù),其實(shí)是同樣遵循上面流程,執(zhí)行了宏任務(wù)
下面是一個(gè)多層次代碼,可進(jìn)行練習(xí):

最終打印結(jié)果:1,7,6,8,2,4,3,5,9,11,10,12