禁止转载,侵权必究!
一.浏览器的事件环
1.浏览器采⽤的就是多进程模型
- 每个⽹⻚运⾏在⾃⼰的进程中,如果⼀个⽹⻚发⽣故障,只会影响该⽹⻚,⽽不会对整个浏览器造成影响。
- 每个⽹⻚都在它⾃⼰的隔离环境中运⾏,因此不同⽹⻚之间的代码不能相互访问。
2. 浏览器进程的组成
- 浏览器主进程(管理浏览器的整体界⾯)
- 渲染进程 (每个⻚⾯⼀个,负责呈现⻚⾯内容及响应⽤户交互)
- ⽹络进程 (加载资源的进程)
- 插件进程(独⽴的进程)
- GPU 绘图进程 (通过 GPU 来处理图形渲染和图形处理的过程)
3.渲染进程
- js 引擎线程 (执⾏ js 代码)
- 渲染线程 (渲染⻚⾯、布局、画⻚⾯)
- ⽹络线程(处理⻚⾯的⽹络请求)
- GPU 线程 (使⽤ GPU 进⾏图形渲染)
- 合成线程(将多个图层合并为单个图像)
- 事件触发线程 (调度任务)
在 js 执⾏的过程中还会创建⼀些其他的线程 (定时器、http 请求、事件)
4.异步任务
将异步任务进行分类
执行时机:(他们的执行时机不同)
- 宏任务: setTimeout ajax 请求 event 事件 setImmediate 脚本
<script>ui 渲染 i/o MessageChannel(消息通道) - 微任务: Promise.then(原生的 promise) mutationObserver(h5 提供的 api, 如根据页面高度把内容填满) process.nextTick
- requestAnimationFrame(动画) requestIDleCallback (react 中 fiber 的实现就是) 可以认为是宏任务?
5.宏任务/微任务执行顺序
- 浏览器内部有很多的宏任务队列 (默认在执行渲染的过程中,会被单独的一个宏任务队列来管理) 所以在剖析代码运行的时候我们就当只有一个宏任务队列
- 宏任务是最新执行的(我要先执行一个脚本) -> 可能会调用一些 webApi -> 成功后会将这些回调放入到宏任务队列中 (不是立刻放入) -> 可能调用了一些主环境提供的方法 promise.then / mutationObserver -> 立刻放入微任务队列中
- 宏任务执行的时候 会对应产生一个微任务队列
- 当当前的宏任务执行完毕后,会清空微任务队列(清空的过程可能会产生 宏任务、微任务,产生的宏任务逻辑和上面一致,产生的微任务会被放到当前队列中)
- 微任务清空后,会在宏任务队列中取出来一个继续执行
宏任务和微任务的调度操作靠的就是 eventLoop 来实现的
二.经典实例
微任务和 GUI 渲染
document.body.style.background = "red";
console.log(1);
Promise.resolve().then(() => {
console.log(2);
document.body.style.background = "yellow";
});
console.log(3);
// 1 3 2 黄色
事件任务
button.addEventListener("click", () => {
console.log("listener1");
Promise.resolve().then(() => console.log("microtask1"));
});
button.addEventListener("click", () => {
console.log("listener2");
Promise.resolve().then(() => console.log("microtask2"));
});
button.click();
// 默认的输出和点击后的输出
// click1() click2() 不会产生对应的宏任务,是在主线程中执行的 // listener1 listener2 task1 task2
// 点击的话: 默认会产生两个宏任务,一次执行 // listener1 task1 listener2 task2
定时器任务
Promise.resolve().then(() => {
console.log("Promise1");
setTimeout(() => {
console.log("setTimeout2");
}, 0);
});
setTimeout(() => {
console.log("setTimeout1");
Promise.resolve().then(() => {
console.log("Promise2");
});
}, 0);
// Promise1 setTimeout1 Promise2 setTimeout2
任务执行
console.log(1);
async function async() {
console.log(2);
await console.log(3); // yield console.log(3)
// Promise.resolve(console.log(3)).then(()=> console.log(4))
console.log(4);
}
setTimeout(() => {
console.log(5);
}, 0);
const promise = new Promise((resolve, reject) => {
console.log(6);
resolve(7);
});
promise.then((res) => {
console.log(res);
});
async();
console.log(8); // 1 6 2 3 8 7 4 5
原来的promise 如果返回的是promise 则会给这个promise再产生一个微任务
Promise.resolve()
.then(() => {
console.log(0);
return new Promise((resolve) => {
// x = Promise.resolve('a') => x.then('a') // queueMicrotask(() =>Promise.resolve('a'))
resolve("a");
});
})
.then((res) => {
console.log(res);
});
Promise.resolve()
.then(() => {
console.log(1);
})
.then(() => {
console.log(2);
})
.then(() => {
console.log(3);
})
.then(() => {
console.log(4);
})
.then(() => {
console.log(5);
});
// 0 1 2 3 a 4 5