基礎概念
- 程式是計算機已經執行的程式,執行緒是作業系統能夠進行運算排程的最小單位,它被包含在程式中.瀏覽器中每開一個Tab頁,就會開啟一個程式,而這個程式又包含了很多執行緒.
- 大家都知道JS是一門單執行緒語言,如果遇到了非常耗時的操作,那麼JS的執行就會受到阻塞,這肯定不是我們想看到的,所以這些耗時的操作,往往不是由JS執行緒所執行的,而是交由瀏覽器中的其他執行緒去完成的,成功之後只要在某個特定的時候進行一個回撥函式即可
- 所以引出了事件迴圈的概念,在事件迴圈中,分兩種任務,分別是巨集任務和微任務
- 巨集任務包含
ajax、setTimeout、setInterval、DOM監聽、UI Rendering
- 微任務包含
Promise的then回撥、 Mutation Observer API、queueMicrotask()等
- 巨集任務包含
- 接下來我們直接就開始練習面試題熟悉熟悉
面試題一
setTimeout(function () {
console.log("setTimeout1");
new Promise(function (resolve) {
resolve();
}).then(function () {
new Promise(function (resolve) {
resolve();
}).then(function () {
console.log("then4");
});
console.log("then2");
});
});
new Promise(function (resolve) {
console.log("promise1");
resolve();
}).then(function () {
console.log("then1");
});
setTimeout(function () {
console.log("setTimeout2");
});
console.log(2);
queueMicrotask(() => {
console.log("queueMicrotask1")
});
new Promise(function (resolve) {
resolve();
}).then(function () {
console.log("then3");
});
- 先解決同步任務
- 輸出
promise1
2
- 輸出
- 開始解決非同步任務中的微任務
- 輸出
then1
queueMicrotask1
then3
- 輸出
- 開始解決非同步任務中的巨集任務
- 輸出
setTimeout1
,在第一個定時器中,又遇到了微任務,那麼接著執行微任務- 輸出
then2
然後輸出then4
- 輸出
- 目光跳出第一個定時器中,看到第二個定時器 開始輸出
setTimeout2
- 輸出
- 最後的完整輸出為
promise1 2 then1 queueMicrotask1 then3 setTimeout1 then2 then4 setTimeout2
面試題二
async function async1() {
console.log('async1 start')
// await非同步函式的返回結果 resolve的結果會作為整個非同步函式的promise的resolve結果->同步程式碼
// await後面的執行程式碼 就會變成.then後面的執行函式->微任務
// 也就是說 console.log('async1 end') 這一段是相當於then方法內的 會被加入微任務中
await async2();
console.log('async1 end')
}
async function async2() {
console.log('async2')
}
console.log('script start')
setTimeout(function () {
console.log('setTimeout')
}, 0)
async1();
new Promise(function (resolve) {
console.log('promise1')
resolve();
}).then(function () {
console.log('promise2')
})
console.log('script end')
- 先執行同步程式碼
- 輸出
script start
async1 start
async2
promise1
script end
- 輸出
- 開始執行微任務
- 輸出
async1 end
promise2
- 輸出
- 最後執行巨集任務
- 輸出
setTimeout
- 輸出
- 完整輸出:
script start
async1 start
async2
promise1
script end
async1 end
promise2
setTimeout
面試題三
Promise.resolve().then(() => {
console.log(0);
//1.直接返回4 微任務不會做任何延遲
// return 4
//2.直接返回Promise.resolve(4) 微任務推遲兩次
// return Promise.resolve(4);
//3.返回thenable物件
return {
then: ((resolve, reject) => {
resolve(4);
})
}
}).then((res) => {
console.log(res)
})
Promise.resolve().then(() => {
console.log(1);
}).then(() => {
console.log(2);
}).then(() => {
console.log(3);
}).then(() => {
console.log(5);
}).then(() => {
console.log(6);
})
這道面試題有些特殊,需要大家記住兩個結論
- 如果返回的是
thenable
物件,那麼微任務會推遲一次,thenable
物件就是實現了Promise.then
的那個函式,具體可看程式碼 - 如果返回的是
Promise.resolve(4)
,那麼微任務會推遲兩次,這個相當於是返回一個Promise
之後又用了resolve
,二者是等價的 - 為了配合大家理解,我給大家畫了幾張圖,大家可以看看
面試題四
本道題是基於node的事件迴圈,和瀏覽器的事件迴圈不一樣,需要記住以下幾點
node的事件迴圈也分巨集任務和微任務
- 巨集任務:
setTimeout、setInterval、IO事件、setImmediate、close事件
- 微任務:
Promise的then回撥、process.nextTick、queueMicrotask
node的每次事件迴圈都是按照以下順序來執行的
- next tick microtask queue
- other microtask queue
- timer queue
- poll queue
- pcheck queue
- close queue
async function async1() {
console.log('async1 start')
await async2()
console.log('async1 end')
}
async function async2() {
console.log('async2')
}
console.log('script start')
setTimeout(function () {
console.log('setTimeout0')
}, 0)
setTimeout(function () {
console.log('setTimeout2')
}, 300)
setImmediate(() => console.log('setImmediate'));
process.nextTick(() => console.log('nextTick1'));
async1();
process.nextTick(() => console.log('nextTick2'));
new Promise(function (resolve) {
console.log('promise1')
resolve();
console.log('promise2')
}).then(function () {
console.log('promise3')
})
console.log('script end')
- 首先執行同步任務
- 輸出
script start
async1 start
async2
promise1
promise2
script end
- 輸出
- 接著執行微任務,因為node會優先執行
nextTick
這個微任務- 所以先輸出
nextTick1
nextTick2
- 在輸出其他微任務,輸出
async1 end
promise3
- 所以先輸出
- 最後執行巨集任務
- 輸出
setTimeout0
setImmediate
- 因為這個定時器延時3ms執行,所以會讓其他的巨集任務先執行完畢,才回去執行這個定時器,所以最後輸出
setTimeout2
- 輸出
- 最後的輸出結果:
script start
async1 start
async2
promise1
promise2
script end
nextTick1
nextTick2
async1 end
promise3
setTimeout0
setImmediate
setTimeout2