事件循环 --任务队列和微任务队列

79 阅读2分钟

事件循环

事件循环是单线程JavaScript为了处理异步操作中执行顺序的问题,通过监听task队列和microtask 队列实现异步操作

Event Loop 通过监听不同队列,当队列中有任务的时候取出任务并进行执行,执行完成后检查是否还有任务,如果哟继续执行,直到为空为止。

最新的W3C准则将任务队列分为微任务队列和任务队列

微任务队列(microtask queue)

先进先出,事件循环中只会拥有一个

任务队列

普通任务分为五个可具有优先级的队列

队列类别(规范术语)常见任务源 / API优先级说明
交互队列(user interaction task queue)click、keydown、pointerup 等 UI 事件最高,浏览器必须优先处理
渲染队列(rendering task queue)requestAnimationFrame、插值动画、ResizeObserver次高,每轮循环最多运行一次
高优先级调度队列(scheduler user-blocking queue)scheduler.postTask(cb, {priority:'user-blocking'})高,由 Prioritized Task Scheduling API 产生
普通/延时队列(generic / timer task queue)setTimeout、setInterval、MessageChannel默认优先级
后台队列(scheduler background queue)scheduler.postTask(cb, {priority:'background'})最低,浏览器空闲时才选

循环过程是通过从高到低五个队列查找第一个非空队列,使用FIFO策略进行获取任务

执行顺序

html中遇到

– 同步脚本:解析器碰到就立即开始; – defer 脚本:DOM 解析完后、渲染前一次性开始; – async 脚本:下载完后作为一次普通 task 被事件循环挑中才开始。

同步脚本中如果网络请求慢了会一直等待脚本的加载

  • 执行同步代码
  • 当执行栈为空,查询microtask 队列,如有执行队头任务
  • 查询任务队列
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Document</title>
  </head>
  <body>
    <script>
      console.log("script start1");
      Promise.resolve().then(() => {
        console.log("promise1");
      });
      setTimeout(() => {
        console.log("setTimeout1");
      }, 0);
      scheduler.postTask(
        () => {
          console.log("postTask1");
        },
        { priority: "user-blocking" }
      );
      /*
        script start1
        promise1
        postTask1
        setTimeout1
       */
    </script>
  </body>
</html>