JAVAScript是單線程的語(yǔ)言,也就是說同一時(shí)間只能進(jìn)行一個(gè)任務(wù),畢竟JavaScript可以操作dom,進(jìn)行ui交互,刪除dom和修改dom同時(shí)進(jìn)行,那么應(yīng)該聽哪一個(gè)?。
正是因?yàn)檫@樣,決定了它是單線程的,但是又說單線程只能在同一個(gè)時(shí)間內(nèi)執(zhí)行一個(gè)任務(wù),這樣豈不是會(huì)造成阻塞?前一個(gè)任務(wù)執(zhí)行很久,后面都卡死了,所以就需要JavaScript的事件循環(huán)機(jī)制來進(jìn)行分配,這樣就會(huì)被分為
宏任務(wù):當(dāng)前調(diào)用棧執(zhí)行的代碼稱為宏任務(wù)
宏任務(wù)有哪些?
- 主代碼塊
- 定時(shí)器 setTimeout、setInterval、setImmediate
- 同步代碼
- I/O
微任務(wù):當(dāng)前宏任務(wù)執(zhí)行完,在下一個(gè)宏任務(wù)開始之前需要執(zhí)行的在隊(duì)列(先進(jìn)先出)里的任務(wù),可理解為回調(diào)事件
微任務(wù)有哪些?
- promise.then
- nextTick
- process
- Object.observe、MutationObserve
- catch finally
運(yùn)行機(jī)制,面試中經(jīng)常會(huì)遇到這樣的題目(讓說出它的執(zhí)行順序):
console.log('1');
setTimeout(function() {
console.log('2');
}, 0);
Promise.resolve().then(function() {
console.log('3');
}).then(function() {
console.log('4');});
console.log('5');
執(zhí)行結(jié)果:1 5 3 4 2
先了解一下運(yùn)行機(jī)制:
1. 在執(zhí)行棧中執(zhí)行一個(gè)宏任務(wù)(程序剛開始執(zhí)行時(shí),會(huì)把整個(gè)代碼當(dāng)成第一個(gè)宏任務(wù))。
2. 執(zhí)行的過程中遇到微任務(wù),先將微任務(wù)添加到微任務(wù)隊(duì)列中(它是屬于本次執(zhí)行的宏任務(wù)中分出來微任務(wù)),遇到宏任務(wù),放到下一個(gè)執(zhí)行棧中。
3. 當(dāng)前宏任務(wù)執(zhí)行完畢,立即執(zhí)行任務(wù)隊(duì)列(先進(jìn)先出的原則)中的微任務(wù)
4. 當(dāng)前微任務(wù)隊(duì)列中的任務(wù)執(zhí)行完畢
5. 接著開始執(zhí)行執(zhí)行棧中的下一個(gè)宏任務(wù),
6. 依次循環(huán)這個(gè)規(guī)律
解析上面的代碼:
1. 整體代碼被當(dāng)成第一個(gè)宏任務(wù),代碼從上到下執(zhí)行
2. 遇到console.log('1');是同步任務(wù),直接執(zhí)行,輸出1
3. 遇到setTimeout,是宏任務(wù),放到執(zhí)行棧中
4. 再往下執(zhí)行,遇到promise,promise的執(zhí)行器里是立即執(zhí)行的,這里沒有執(zhí)行器,而是有then,then是微任務(wù),先放到本次宏任務(wù)的微任務(wù)隊(duì)列中,then后面返回的也是一個(gè)promise,第二個(gè)then也是一個(gè)微任務(wù),放到微任務(wù)隊(duì)列中。
5.接著遇到同步代碼console.log('5');直接輸出5,這時(shí)候,第一輪宏任務(wù)執(zhí)行完畢
6.需要去執(zhí)行本輪它產(chǎn)生的微任務(wù),先進(jìn)先出的原則,輸出3 4
7.這時(shí)候循環(huán)的第一周期就結(jié)束了
8.再去執(zhí)行棧中的第二個(gè)宏任務(wù),輸出 2
來個(gè)難的練習(xí)一下:
console.log('1');
setTimeout(function() {
console.log('2');
process.nextTick(function() {
console.log('3');
})
new Promise(function(resolve) {
console.log('4');
resolve();
}).then(function() {
console.log('5')
})
})
process.nextTick(function() {
console.log('6');
})
new Promise(function(resolve) {
console.log('7');
resolve();
}).then(function() {
console.log('8')
})
setTimeout(function() {
console.log('9');
process.nextTick(function() {
console.log('10');
})
new Promise(function(resolve) {
console.log('11');
resolve();
}).then(function() {
console.log('12')
})
})
結(jié)果:1 7 6 8 2 4 3 5 9 11 10 12
process.nextTick 需要放在node環(huán)境下才能執(zhí)行:
加上async和await
async function async1() {
console.log('1');
let a = await async2();
console.log(a);
console.log('2');
}
async function async2() {
return new Promise((resolve) => {
setTimeout(() => {
resolve('data');
}, 0);
});
}
console.log('3');
setTimeout(function () {
console.log('4');
}, 0);
async1();
new Promise(function (resolve) {
console.log('5');
resolve();
}).then(function () {
console.log('6');
});
console.log('7');
// 3 1 5 7 6 4 data 2
提示:
- await是一個(gè)讓出線程的標(biāo)志。
- 正常情況下, await 命令后面是一個(gè) Promise 對(duì)象。如果不是,會(huì)被轉(zhuǎn)成一個(gè)立即 resolve 的 Promise 對(duì)象
- 當(dāng)函數(shù)執(zhí)行的時(shí)候, 一旦遇到 await 就會(huì)先返回,跳出函數(shù)體執(zhí)行外部的代碼,等到異步操作完成,再接著執(zhí)行函數(shù)體內(nèi)后面的代碼。