浏览器是 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