# 浏览器中 Frame 与 Event Loop 的关系是什么
# 前言
浏览器的组成中有两大引擎,
js引擎
和渲染引擎
,分别负责js代码
执行和页面的渲染。Frame
即帧
,1 帧可以理解成一张图,连接提供很多帧就形成了所谓的动画,常说的fps
即帧率
(Frame per Second
),是指1s
时间渲染引擎
给显示器提供多少的帧
。Event Loop
即 事件循环。js
是单线程的,通过Event Loop
实行的异步操作。
# 浏览器中的Frame
Frame
指渲染引擎每隔 16ms (默认 60fps) 将渲染树渲染、合成位图的结果
# 浏览器中的Event Loop
每次 Event Loop
是 js引擎
执行的一个周期,执行过程中可能依赖渲染引擎
的执行结果,比如访问 DOM
和 CSSOM
,也可能影响渲染引擎
去绘制帧
,比如说调用 requestAnimationFrame
,在每个帧开始绘制时执行一段回调函数(通常包含影响渲染结果的代码),因此 Frame
和 Event
Loop 是相对独立的,但是 Event Loop
中执行代码可能依赖或者影响 Frame
。
js 为什么是单线程
正因为js代码
执行可能依赖或者影响 Frame
渲染,所以如果是多线程,两个线程同时对同一个 DOM 元素,一个要修改,一个要删除,就会出问题
# js 如何实现异步的 -- 即(Event Loop 事件循环)
task任务
js 代码分为同步任务和异步任务,异步任务中又分为 宏任务(macrotask)
和 微任务(microtask)
。微任务执行优先于宏任务。
事件循环任务的执行过程:
- 当从执行栈中拿出的任务是同步的,就立即执行
- 是宏任务,放入宏任务队列
- 是微任务,放入微任务队列
- 当当前执行栈没有任务了,就从微任务队列中拿任务执行
- 执行微任务中的全部同步代码
- 如果遇到宏任务,就继续放入宏任务队列
- 如果遇到微任务,就放入微任务队列的末尾等待执行
- 当微任务队列中没有任务,就从宏任务队列中拿任务执行
- 执行宏任务中的全部同步代码
- 如果遇到宏任务,就放入宏任务队列中,等待下一次循环时执行
- 如果遇到微任务,就放入微任务队列
- 当宏任务中的同步代码执行完,就从微任务队列总拿任务执行
- 回到第5-7步的逻辑
- 最后当宏任务中的微任务也执行完了,就是微任务队列没有任务了,一次循环结束。
- 回到第1步的逻辑
提示
宏任务(macrotask)
中的同步宏任务会等到下一次循环执行。
而微任务(microtask)
中的宏任务会在当前一次事件循环,微任务执行完毕后执行。
常见的宏任务:setTimeout,setInterval,MessaageChannel,setImmediate,I/O(键盘,网络,鼠标),UI 渲染
常见的微任务:Promise.then/.catch,process.nextTick,Object.observe,Mutation.Observer
考虑一下代码输出
console.log("同步-start-1");
const interval = setInterval(() => {
console.log("宏任务-interval-2");
}, 0);
setTimeout(() => {
console.log("宏任务-timeout-start-3");
setTimeout(() => {
console.log("宏任务/宏任务-4");
}, 0);
Promise.resolve()
.then(() => {
console.log("宏任务/微任务-5");
setTimeout(() => {
console.log("宏任务/微任务/宏任务-6");
}, 0);
Promise.resolve().then(() => {
console.log("宏任务/微任务/微任务-7");
});
})
.then(() => {
console.log("宏任务/微任务-8");
})
.then(() => {
setTimeout(() => {
console.log("宏任务/微任务/宏任务-start-9");
Promise.resolve()
.then(() => {
console.log("宏任务/微任务/宏任务/微任务-10");
})
.then(() => {
console.log("宏任务/微任务/宏任务/微任务-11");
})
.then(() => {
clearInterval(interval);
});
console.log("宏任务/微任务/宏任务-end-12");
}, 0);
});
console.log("宏任务-timeout-end-13");
}, 0);
Promise.resolve()
.then(() => {
console.log("微任务-14");
})
.then(() => {
console.log("微任务-15");
});
Promise.resolve()
.then(() => {
console.log("微任务-16");
})
new Promise(() => {
console.log("同步-end-17");
});
答案:1,17,14,16,15,2,3,13,5,7,8, (第一次事件循环) 2,4,6,9,12,10,11(第二次事件循环)
const btn = document.createElement("button");
btn.innerText = "点击";
btn.addEventListener("click", () => {
Promise.resolve().then(() => {
console.log("微任务-1");
});
console.log("同步任务-2");
});
btn.addEventListener("click", () => {
Promise.resolve().then(() => {
console.log("微任务-3");
});
console.log("同步任务-4");
});
document.body.appendChild(btn);
btn.click();//模拟点击
//模拟按钮点击
//同步任务-2
//同步任务-4
//微任务-1
//微任务-3
//用鼠标点击按钮 会创建2个宏任务分别执行
//同步任务-2
//微任务-1
//同步任务-4
//微任务-3
← http和浏览器缓存 vmware虚拟机 →