瀏覽器是 multi-process,一個瀏覽器只有一個 Browser Process,負責管理 Tabs、協調其餘 process 和 Renderer process 存至 memory 內的 Bitmap 繪製到頁面上的(pixel);在 Chrome中,一個 Tab 對應一個 Renderer Process,Renderer process 是 multi-thread,其中 main thread 負責頁面渲染(GUI render engine)執行 JS (JS engine)和 event loop;network component 能夠開2~6個 I/O threads 平行去處理。node
深刻演示:loupegit
https://github.com/latentflip/loupegithub
// 函數執行棧演繹-->函數調用過程面試
function fun3() {
console.log('fun3')
}
function fun2() {
fun3();
}
function fun1() {
fun2();
}
fun1();複製代碼
問題1:若是咱們在瀏覽器控制檯中運行'foo'函數,是否會致使堆棧溢出錯誤?promise
function foo() {
setTimeout(foo, 0); // 是否存在堆棧溢出錯誤?
};複製代碼
function foo() {
foo() // 是否存在堆棧溢出錯誤?
};
foo();複製代碼
問題2:若是在控制檯中運行如下函數,頁面(選項卡)的 UI 是否仍然響應瀏覽器
function foo() {
return Promise.resolve().then(foo);
};複製代碼
alert(x);
var x = 10;
alert(x);
x = 20;
function x() {};
alert(x); 複製代碼
一個函數執行棧、一個事件隊列和一個微任務隊列。bash
每從事件隊列中取一個事件時有微任務就把微任務執行完,而後纔開始執行事件架構
宏任務,macrotask,也叫tasks。 一些異步任務的回調會依次進入macro task queue,等待後續被調用,這些異步任務包括:異步
微任務,microtask,也叫jobs。 另外一些異步任務的回調會依次進入micro task queue,等待後續被調用,這些異步任務包括:ide
(注:這裏只針對瀏覽器和NodeJS)
注意:Promise構造函數裏的代碼是同步執行的。
setTimeout(()=> {
console.log(1)
Promise.resolve(3).then(data => console.log(data))
}, 0)
setTimeout(()=> {
console.log(2)
}, 0)複製代碼
console.log('script start');
setTimeout(function() {
console.log('setTimeout');
}, 0);
Promise.resolve().then(function() {
console.log('promise1');
}).then(function() {
console.log('promise2');
});複製代碼
瀏覽器端:jakearchibald.com/2015/tasks-…
console.time("start")
setTimeout(function () {
console.log(2);
}, 10);
new Promise(function (resolve) {
console.log(3);
resolve();
console.log(4);
}).then(function () {
console.log(5);
console.timeEnd("start")
});
console.log(6);
console.log(8);
requestAnimationFrame(() => console.log(9))複製代碼
Node.js的Event Loop過程:
注意:new Promise() 構造函數裏面是同步代碼,而非微任務。
Promise.resolve('123').then(res=>{ console.log(res)})
process.nextTick(() => console.log('nextTick'))複製代碼
//順序 nextTick 123
//很明顯 nextTick快
解釋:
promise.then 雖然和 process.nextTick 同樣,都將回調函數註冊到 microtask,但優先級不同。process.nextTick 的 microtask queue 老是優先於 promise 的 microtask queue 執行。
setImmediate(callback[, ...args])
Schedules the "immediate" execution of the callback
after I/O events' callbacks.
setImmediate()方法用於中斷長時間運行的操做,並在完成其餘操做後當即運行回調函數。
setTimeout 和 setImmediate 執行順序不固定 取決於node的準備時間
setTimeout(() => {
console.log('setTimeout')
}, 0)
setImmediate(() => {
console.log('setImmediate')
})複製代碼
運行結果:
setImmediate
setTimeout
或者:
setTimeout
setImmediate
爲何結果不肯定呢?
解釋:
setTimeout/setInterval 的第二個參數取值範圍是:[1, 2^31 - 1],若是超過這個範圍則會初始化爲 1,
即 setTimeout(fn, 0) === setTimeout(fn, 1)。
咱們知道 setTimeout 的回調函數在 timer 階段執行,setImmediate 的回調函數在 check 階段執行,event loop 的開始會先檢查 timer 階段,可是在開始以前到 timer 階段會消耗必定時間;
因此就會出現兩種狀況:
setTimeout(() => {
console.log('setTimeout')
}, 0)
setImmediate(() => {
console.log('setImmediate')
})
const start = Date.now()
while (Date.now() - start < 10);複製代碼
運行結果必定是:
setTimeout
setImmediate
const fs = require('fs')
fs.readFile(__filename, () => {
setTimeout(() => {
console.log('setTimeout')
}, 0)
setImmediate(() => {
console.log('setImmediate')
})
})複製代碼
運行結果:
setImmediate
setTimeout
解釋:
fs.readFile 的回調函數執行完後:
註冊 setTimeout 的回調函數到 timer 階段
註冊 setImmediate 的回調函數到 check 階段
event loop 從 pool 階段出來繼續往下一個階段執行,剛好是 check 階段,因此 setImmediate 的回調函數先執行
本次 event loop 結束後,進入下一次 event loop,執行 setTimeout 的回調函數
因此,在 I/O Callbacks 中註冊的 setTimeout 和 setImmediate,永遠都是 setImmediate 先執行。
console.time("start")
setTimeout(function () {
console.log(2);
}, 10);
setImmediate(function () {
console.log(1);
});
new Promise(function (resolve) {
console.log(3);
resolve();
console.log(4);
}).then(function () {
console.log(5);
console.timeEnd("start")
});
console.log(6);
process.nextTick(function () {
console.log(7);
});
console.log(8);
// requestAnimationFrame(() => console.log(9))
複製代碼
運行結果以下:
運行時分析
setTimeout(() => console.log('timeout1'));
setTimeout(() => {
console.log('timeout2')
Promise.resolve().then(() => console.log('promise resolve'))
});
setTimeout(() => console.log('timeout3'));
setTimeout(() => console.log('timeout4'));複製代碼
github.com/nodejs/node… MacroTask and MicroTask execution order
blog.insiderattack.net/new-changes…
github.com/nodejs/node… timers: run nextTicks after each immediate and timer