JS 是一門單線程語言,最新的 HTML5 中新增了 Web Worker,但 JavaScript 是單線程這一核心仍未改變。
JS 單線程基于事件循環(huán):分為異步和同步。同步執(zhí)行完,在執(zhí)行異步中的內(nèi)容。
- 同步的進(jìn)入主線程,異步的進(jìn)入 Event Table(事件列表) 并注冊函數(shù)。
- 當(dāng)指定的事情完成時(shí),Event Table 會(huì)將這個(gè)函數(shù)移入 Event Queue(事件隊(duì)列)。
- 主線程內(nèi)的任務(wù)執(zhí)行完畢為空,會(huì)去 Event Queue 讀取對應(yīng)的函數(shù),進(jìn)入主線程執(zhí)行。
- 上述過程會(huì)不斷重復(fù),也就是常說的 Event Loop(事件循環(huán))。
更加精確的定義
任務(wù)隊(duì)列又分為宏任務(wù)隊(duì)列和微任務(wù)隊(duì)列。
- 宏任務(wù)(MacroTask):整體代碼 Script、UI 渲染、I/O、
postMessage、MessageChannel、requestAnimationFrame、setTimeout、setInterval、setImmediate(Node.js 環(huán)境) - 微任務(wù)(MicroTask):Promise 的回調(diào)函數(shù)(
then()、catch()、finally())、process.nextTick(Node.js 環(huán)境)、MutaionObserver
事件循環(huán)
- 進(jìn)入腳本執(zhí)行宏任務(wù),自上而下運(yùn)行
- 遇到同步代碼按順序執(zhí)行,遇到宏任務(wù)放入宏任務(wù)隊(duì)列,遇到微任務(wù)放入微任務(wù)隊(duì)列
- 執(zhí)行完當(dāng)前宏任務(wù),執(zhí)行微任務(wù)中執(zhí)行完并正在等待執(zhí)行的任務(wù)
- 執(zhí)行下一個(gè)宏任務(wù),這樣周而復(fù)始的執(zhí)行順序被稱為事件循環(huán)
注意:JS 的執(zhí)行順序和聲明以及引用的順序有關(guān),先聲明的順序先執(zhí)行,后聲明的順序后執(zhí)行(如果有兩個(gè)同名方法,后聲明的會(huì)覆蓋先聲明的)
題目
// 以下代碼在 Node 環(huán)境運(yùn)行:process.nextTick 由 Node 提供
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")
})
})
// 最終輸出:1 7 6 8 2 4 3 5 9 11 10 12
注意:Node 的
process.nextTick有專門的 nextTick 隊(duì)列運(yùn)行,它總是在其他微任務(wù)之前運(yùn)行。