JavaScript 事件循环再深入

4 阅读1分钟

前言

很多人学完同步、异步、Promise 之后,还是会对执行顺序感到困惑。
比如:

  • 为什么 Promise.thensetTimeout先执行?

  • async/await 到底算同步还是异步?

  • 宏任务和微任务到底怎么执行?

这些问题都和 事件循环(Event Loop)  有关。


一、JavaScript的主线程是单线程的

JavaScript 主线程同一时间只能执行一件事。
为了不让耗时任务阻塞主线程,运行环境提供了异步机制。


二、几个核心概念

1)调用栈(Call Stack)

执行同步代码。

2)宏任务队列(Macrotask Queue)

如:

  • setTimeout
  • setInterval
  • DOM 事件

3)微任务队列(Microtask Queue)

如:

  • Promise.then
  • catch
  • finally
  • queueMicrotask

三、执行规则

事件循环大致流程:

  1. 执行同步代码
  2. 同步代码执行完后,清空微任务队列
  3. 执行一个宏任务
  4. 再清空微任务
  5. 不断循环

四、经典例子

console.log('1');

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

Promise.resolve().then(() => {
  console.log('3');
});

console.log('4');

输出:

1
4
3
2

原因:

  • 14 是同步

  • Promise.then 是微任务

  • setTimeout是宏任务

  • 同步结束后先执行微任务,再执行宏任务


五、async/await 和事件循环

async function test() {
  console.log('a');
  await Promise.resolve();
  console.log('b');
}

console.log('1');
test();
console.log('2');

输出:

1
a
2
b

原因:

  • await 前面的代码同步执行

  • await后面的代码相当于放入微任务


六、总结

事件循环的核心记住一句话:

同步代码先执行,微任务优先于宏任务。