Skip to content

JavaScript 事件循环

事件循环(Event Loop)是 JavaScript 处理异步任务的核心机制,它负责协调 同步代码、异步回调、微任务、宏任务 的执行顺序。

确保 JavaScript 在单线程环境下仍能搞笑的处理异步操作,如 定时器、AJAX、Promise、事件监听 等。

TIP

JavaScript 是单线程的。它所能进行异步操作依赖于浏览器的其他线程,与其本身并无直接关系。

栗子:

当进行AJAX操作时,JS 会将它交给浏览器的 AJAX线程,在执行结束后,回调函数会被放入 任务队列 中,待JS线程空闲时,再从队列中取出回调函数并执行。

模型涉及的容器

  • 源代码:存放源函数;
  • 调用栈:取得函数,在此处进行执行;
  • 异步线程(Ajax线程、定时器线程等):存放异步操作,结束后会将回调放入任务队列中;
  • 任务队列:用于存放异步任务的回调,分为 微任务队列宏任务队列
    • 微任务队列:只有一个,存放微任务
      • promise.then()、promise.catch()
      • new MutaionObserver()
      • process.nextTick()
    • 宏任务队列:可能存在多个,存放宏任务,如 setTimeout、setInterval、DOM 事件等;
      • setTimeout()
      • setInterval()
      • setImmediate()
  • 事件循环:不断检查调用栈是否为空,如果为空,就会执行任务队列中的任务(优先执行微任务);

事件循环执行顺序

1.从 源代码 中拿取函数,如果是 同步函数 ,直接执行,如果是 异步函数 则放入对应的异步线程中;

2.在调用栈中执行同步代码,执行完成后出栈;

3.异步函数执行结束后将回调放入任务队列中,任务队列分为 微任务队列宏任务队列

4.循环检查如果调用栈为空,则从 任务队列 中取出任务,放入 调用栈 中执行,遵循 微任务优先 的原则

举个栗子

js
console.log('同步代码1');

setTimeout(() => {
    console.log('setTimeout');
}, 0);

Promise.resolve().then(() => {
    console.log('同步代码2');
}).then(() => {
    console.log('promise');
});

console.log('同步代码3');

// 执行结果:
// 同步代码1
// 同步代码2
// 同步代码3
// promise
// setTimeout

执行结果分析

在代码中由 同步代码异步代码 组成。

其中 setTimeout() 在执行结束后会被放入 宏队列,而 Promise.resolve().then() 会被放入 微任务队列 中。

在执行时,首先执行 同步代码,因此最先输出:同步代码1、同步代码2、同步代码3。

同步代码执行结束后,调用栈清空,这时会从任务队列中拿取函数,因为优先拿取 微任务,因此输出:promise

调用栈再次清空后,再次从 任务队列 中拿取函数,此时没有 微任务,因此拿去 宏任务,输出:setTimeout

总结

  • 事件循环负责调度 同步任务、微任务和宏任务的执行顺序。
  • 同步代码限制性,微任务优先于宏任务。
  • async/awaitPromise 的语法糖,await 后代码会进入微任务。