背景
进程
- 如果把浏览器比喻成一个公司, 那么一个部门(标签页)就是一个进程
- 大多数app都只有一个进程, 浏览器比较特殊
线程
- 如果把浏览器比喻成一个公司, 那么一个部门就是一个进程, 每个部门里的员工就是一个线程
- 如果说一根筷子🥢就是一个线程, 那么一把筷子就是一个进程
浏览器的进程有:
- 渲染进程, 子进程, 处理 html css js
- 网络进程, 子进程, 负责网络请求
- 浏览器主进程, 董事会
- 每一个tab对应一个进程, 每打开一个tab就创建一个进程, 未来将不会采用这种模式
- 未来趋势: 每一个域名对应一个进程, 打开bilibili创建一个进程, 打开youtobe创建一个进程
渲染主线程
- 渲染主线程是一个非常繁忙的员工, 他既要处理html, 又要处理css, 还要处理js
- 单线程, 工作量巨大...
- 为了更好的完成工作, 他采用了一种叫
事件循环的工作方式
JS为什么是单线程
- 因为他执行在浏览器的渲染主线程中
- 而浏览器的渲染主线程只有一个
事件循环
微任务队列
- chrom浏览器中有两个队列
微任务队列其他很多队列 - 微任务队列是所有队列中优先级最高的队列
- 这里我们将
其他很多队列统称为宏任务队列
宏任务队列
- 延时队列:
setTimeout - 交互队列:
onClick - ...还有很多记不起名的队列
- 以上队列统称为
宏任务队列 - 为了方便理解,下面我们只考虑两个队列:
宏任务队列,微任务队列
伪代码模拟
- 渲染主线程在执行
同步任务时会产生一些宏任务,微任务 - 其他如事件监听线程, 也会产生一些
宏任务 - 这些异步任务会被存放到
宏任务队列,微任务队列 - 渲染主线程执行完
同步任务后 - 优先执行
微任务, - 清空
微任务队列后再执行宏任务
const microTaskQueue = []
const macroTaskQueue = []
// 染主线程在执行同步任务时会产生一些宏任务, 微任务
// 这些异步任务会被存放到宏任务队列, 微任务队列
// try {} catchMicor {} 是伪代码, 表示捕获微任务
// try {} catchMacor {} 是伪代码, 表示捕获宏任务
function run(fn: Function) {
try {
fn()
} catchMicro (microFn) {
microTaskQueue.push(microFn)// 执行过程中产生了微任务, 就放到微任务队列
} catchMicro (macroFn) {
macroTaskQueue.push(macroFn)// 执行过程中产生了宏任务, 就放到宏任务队列
}
}
// loop 的回调函数将不停的执行
loop(() => {
if (microTaskQueue.length) {
const fn = microTaskQueue.shift()
fn()
}
else {
const fn = macroTaskQueue.shift()
fn && fn()
}
})
观察渲染阻塞
优先级
微任务队列大于render队列,render队列大于延时队列,延时队列最低
const p = document.createElement("p").innerHTML = "new paragraph";
document.body.appendChild(p);
setTimeout(() => {
const list = document.getElementsByTagName("p");
console.log("timeout----", list.length);
alert("宏任务阻塞");
});
Promise.resolve().then(() => {
const list = document.getElementsByTagName("p");
console.log("promise.then----", list.length);
alert("微任务阻塞");
});