浏览器运行机制
浏览器内核¶
- Chrome, Safari: webkit
- firefox: Gecko
- IE: Trident
- 360: webkit + Trident
重要内核模块¶
进程:程序的一次执行,占用独立的内存空间。
线程:进程内的一个独立执行单元,共享进程的内存空间。
- js 引擎:负责 js 编译与运行(主线程)
- 文档(html/css)解析模块:负责文本解析(主线程)
- DOM/CSS模块:负责 DOM/CSS 树(主线程)
- 布局和渲染模块:负责页面布局和绘制(主线程)
- 事件响应模块(子线程)
- 定时器模块(子线程)
- 网络请求模块(子线程)
JS引擎¶
- 处理 JavaScript 脚本的虚拟机,如 Chrome 的 V8 引擎
- 堆 内存分配发生的地方
- 栈 函数调用时会形一个个栈帧(frame)
JS是单线程的¶
JS is single-threaded,which does only one thing at a given time.
- JS 只有一个主线程,一次只能执行一段代码。
- 浏览器是事件驱动的,浏览器中很多行为是异步的,会创建事件并放入执行队列中。
- 一个浏览器至少实现三个常驻线程:
- JS 引擎线程
- GUI 渲染线程
- 事件触发线程
执行栈¶
- 函数执行时,会生成新的 execution context(执行上下文),推入执行栈中
- running execution context(正在执行的上下文)始终处于栈顶
- 函数执行完后,它的执行上下文会从栈弹出
事件循环与任务队列¶
- Event Loop是由javascript宿主环境(像浏览器)来实现的;
- WebAPIs是由C++实现的浏览器创建的线程,处理诸如DOM事件、http请求、定时器等异步事件;
- JavaScript 的并发模型基于"事件循环";
- Callback Queue(Event Queue 或者 Message Queue) 任务队列,存放异步任务的回调函数
事件循环¶
"Event Loop is a programming construct that waits for and dispatches events or messages in a program."
涉及两个线程:一个负责程序本身的运行,称为 主线程;一个负责主线程与其他进程(主要各种I/O操作)的通信,被称为 Event Loop 线程 或消息线程。
- 函数入栈,当Stack中执行到异步任务的时候,就将他丢给WebAPIs,接着执行同步任务,直到Stack为空;
- 在此期间WebAPIs完成这个事件,把回调函数放入CallbackQueue中等待;
- 当执行栈为空时,Event Loop把Callback Queue中的一个任务放入Stack中,回到第1步
// 例子
var start = new Date();
setTimeout(function cb(){
console.log("时间间隔:", new Date() - start+'ms');
},500);
while(new Date() - start < 1000){};
- main(Script) 函数入栈,start变量开始初始化
- setTimeout入栈,出栈,丢给WebAPIs,开始定时500ms;
- while循环入栈,开始阻塞1000ms;
- 500ms过后,WebAPIs把cb()放入任务队列,此时while循环还在栈中,cb()等待;
- 又过了500ms,while循环执行完毕从栈中弹出,main()弹出,此时栈为空,Event Loop,cb()进入栈,log()进栈,输出'时间间隔:1003ms',出栈,cb()出栈
任务队列¶
- 宏任务(Macrotasks)
- setTimeout
- setInterval
- setImmediate
- I/O
- UI rendering
- 微任务(Microtasks)
- process.nextTick
- promises
- Object.observe
- MutationObserver
async function async1() {
console.log("async1 start");
await async2();
console.log("async1 end");
}
async function async2() {
console.log( 'async2');
}
debugger
console.log("script start");
setTimeout(function () {
console.log("settimeout");
},0);
async1();
console.log("script middle");
new Promise(function (resolve) {
console.log("promise1");
resolve();
}).then(function () {
console.log("promise2");
});
乱入¶
// 宏任务
requestAnimationFrame(() => console.log('requestAnimationFrame 执行'));
// 宏任务
setTimeout(() => console.log('setTimeout 执行'));
// 微任务
Promise.resolve().then(() => console.log('Promise.resolve 执行'));
// 微任务
queueMicrotask(() => console.log('queueMicrotask 执行'));
谷歌浏览器的
queueMicrotask
类似 nodejs 的process.nextTick
。