# 浏览器中 Frame 与 Event Loop 的关系是什么

# 前言

  1. 浏览器的组成中有两大引擎,js引擎渲染引擎,分别负责js代码执行和页面的渲染。

  2. Frame,1 帧可以理解成一张图,连接提供很多帧就形成了所谓的动画,常说的fps帧率Frame per Second),是指1s时间渲染引擎给显示器提供多少的

  3. Event Loop即 事件循环。

  4. js 是单线程的,通过Event Loop实行的异步操作。

# 浏览器中的Frame

Frame

指渲染引擎每隔 16ms (默认 60fps) 将渲染树渲染、合成位图的结果

# 浏览器中的Event Loop

每次 Event Loopjs引擎执行的一个周期,执行过程中可能依赖渲染引擎的执行结果,比如访问 DOMCSSOM,也可能影响渲染引擎去绘制,比如说调用 requestAnimationFrame,在每个帧开始绘制时执行一段回调函数(通常包含影响渲染结果的代码),因此 FrameEvent Loop 是相对独立的,但是 Event Loop 中执行代码可能依赖或者影响 Frame

js 为什么是单线程

正因为js代码执行可能依赖或者影响 Frame渲染,所以如果是多线程,两个线程同时对同一个 DOM 元素,一个要修改,一个要删除,就会出问题

# js 如何实现异步的 -- 即(Event Loop 事件循环)

task任务

js 代码分为同步任务和异步任务,异步任务中又分为 宏任务(macrotask)微任务(microtask)。微任务执行优先于宏任务。

事件循环任务的执行过程:

  1. 当从执行栈中拿出的任务是同步的,就立即执行
  2. 是宏任务,放入宏任务队列
  3. 是微任务,放入微任务队列
  4. 当当前执行栈没有任务了,就从微任务队列中拿任务执行
  5. 执行微任务中的全部同步代码
  6. 如果遇到宏任务,就继续放入宏任务队列
  7. 如果遇到微任务,就放入微任务队列的末尾等待执行
  8. 当微任务队列中没有任务,就从宏任务队列中拿任务执行
  9. 执行宏任务中的全部同步代码
  10. 如果遇到宏任务,就放入宏任务队列中,等待下一次循环时执行
  11. 如果遇到微任务,就放入微任务队列
  12. 当宏任务中的同步代码执行完,就从微任务队列总拿任务执行
  13. 回到第5-7步的逻辑
  14. 最后当宏任务中的微任务也执行完了,就是微任务队列没有任务了,一次循环结束。
  15. 回到第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
Last Updated: 5/14/2022, 11:15:10 PM