Skip to content

事件循环 Event Loop

浏览器为了协调同步和异步任务制定的一种工作机制,主线程先依次执行同步任务,同步任务中有一些异步回调,将其推入到任务队列

宏任务与微任务

宏任务 Macro-Task

宿主环境(运行 JS 的环境,例如浏览器)提供的叫宏任务,主要包括创建⽂档对象、解析HTML、执⾏主线JS代码以及各种事件如⻚⾯加载、输⼊、⽹络事件和定时器等

微任务 Micro-Task

语言标准(例如ECMA)提供的叫微任务,微任务的例⼦有 Promise 回调函数DOM变化等。

执行顺序为:一个宏任务 =》所有微任务 =》下一个宏任务,可以点此体验一下

requestAnimationFrame

window.requestAnimationFrame() 该方法需要传入一个回调函数作为参数,该回调函数会在浏览器下一次重绘之前执行,也属于异步执行的方法,但是该方法既不是宏任务,也不是微任务

事件循环的过程

  1. 执行完同步任务
  2. 检查 microtask queues 微任务队列是否为空,非空则到第 3 步,为空到第 4 步
  3. 执行 microtask queues 微任务队列中的所有任务
  4. 检查 macrotask queues 宏任务队列是否为空,非空则到第 5 步,为空到第 6 步
  5. 取出 macrotask queues 中的一个任务,在执行栈执行,执行完成后返回到第 2 步
  6. 执行视图更新

PromisesetTimeout 举例,执行以下代码:

js
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,其事件循环过程如下所示:

  1. 一开始执行栈的同步任务执行完毕,检查微任务队列
  2. 清空微任务队列 ,此时输出 Promise1,并将 setTimeout2 放入宏任务队列,此时宏任务队列中任务是 [ setTimeout1setTimeout2 ]
  3. 从宏任务队列中取出第一个任务执行,输出 setTimeout1 ,并将 Promise2 放入微任务队列,此时微任务队列中任务是 [ Promise2 ]
  4. 接着又检查并清空微任务队列,输出 Promise2
  5. 再次清空 microtask queues 后查询宏任务队列[ setTimeout2 ],取出第一个任务执行,,输出 setTimeout2