前端面试宝典

JS 单线程、事件循环、微任务、宏任务 一次性讲透

这是 JS 最核心、面试必问、开发必懂的底层原理!我用最通俗的大白话 + 流程图 + 代码示例,让你彻底吃透,再也不晕。


一、先搞懂:JS 为什么是【单线程】?

一句话:
JS 是单线程的——同一时间只能做一件事!

为什么要设计成单线程?

因为 JS 主要运行在浏览器里,负责操作 DOM。
如果是多线程:

  • 线程 A 要修改 DOM
  • 线程 B 要删除 DOM
    浏览器就懵了,不知道听谁的。

所以 JS 从诞生就是单线程,这是它的灵魂,也是一切异步逻辑的根源。


二、单线程遇到的问题:阻塞!

单线程意味着:任务必须排队,一个做完才能做下一个

如果有个任务很慢(比如定时器、网络请求、读取文件),后面所有代码都会卡住不动,页面就会卡死。

为了解决这个问题,JS 设计了 同步任务 + 异步任务 + 事件循环(Event Loop)


三、核心概念:同步 vs 异步

1. 同步任务(Synchronous)

  • 主线程排队执行
  • 必须等上一个完成,下一个才开始
  • 会阻塞代码
console.log(1);
console.log(2); // 必须等1执行完才运行

2. 异步任务(Asynchronous)

  • 不进入主线程,先进入 任务队列
  • 等主线程空闲了,才会拿过来执行
  • 不会阻塞代码(定时器、ajax、点击事件等)
setTimeout(() => {
  console.log("我是异步"); // 最后执行
}, 0);
console.log("我是同步"); // 先执行

四、最重要:事件循环(Event Loop)——JS 的执行规则

事件循环就是 JS 执行代码的死循环流程,是异步的底层机制。

一句话总结执行顺序:

先执行所有同步代码 → 再执行所有微任务 → 最后执行宏任务 → 循环往复

完整流程(背下来就无敌):

  1. 执行主线程的同步代码(调用栈)
  2. 同步代码执行完,栈空了
  3. 清空所有微任务队列(全部执行完)
  4. 去宏任务队列取一个宏任务执行
  5. 执行完,再次清空所有微任务
  6. 渲染页面
  7. 回到第4步,循环……

五、终极难点:微任务 vs 宏任务

1. 宏任务(Macrotask)

  • 队列每次只执行一个
  • 由宿主环境(浏览器/Node)提供
  • 执行慢,优先级低

常见宏任务:

  • setTimeout / setInterval
  • 浏览器 I/O 操作(ajax、点击事件)
  • script 标签整体代码
  • UI 渲染

2. 微任务(Microtask)

  • 队列一次性全部清空
  • 由 JS 引擎自身提供
  • 执行快,优先级远高于宏任务

常见微任务:

  • Promise.then / catch / finally
  • process.nextTick(Node 独有)
  • MutationObserver(浏览器独有)
  • queueMicrotask()

六、一句话口诀(面试必背)

同步先执行,微任务次之,宏任务最后!
同一轮循环:所有微任务 > 一个宏任务


七、最经典考题(测一测你懂了没)

看代码,说出输出顺序:

console.log('1 同步');

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

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

console.log('4 同步');

正确输出:

1 同步
4 同步
3 微任务
2 宏任务

执行步骤解析:

  1. 先执行同步:1、4
  2. 同步完,清空微任务:3
  3. 最后执行宏任务:2

八、升级版考题(嵌套)

console.log('1');

setTimeout(() => {
  console.log('2');
  Promise.resolve().then(() => {
    console.log('3');
  });
}, 0);

Promise.resolve().then(() => {
  console.log('4');
  setTimeout(() => {
    console.log('5');
  }, 0);
});

console.log('6');

正确输出:

1
6
4
2
3
5

执行逻辑:

  1. 同步:1、6
  2. 微任务:4(里面的宏任务5进入队列)
  3. 第一个宏任务:2
  4. 宏任务2里的微任务:3
  5. 下一个宏任务:5

九、终极总结

执行优先级

同步代码 > 微任务(全部) > 宏任务(一个)

分类速记

  • 宏任务:定时器、ajax、点击事件、script
  • 微任务:Promise.then、catch、finally、queueMicrotask

核心一句话

JS 是单线程,事件循环来调度,微任务永远比宏任务先执行!

总结

  1. 单线程:同一时间只做一件事
  2. 事件循环:JS 的执行调度机制
  3. 执行顺序:同步 → 微任务 → 宏任务
  4. 微任务:一次性清空,优先级极高
  5. 宏任务:每次只执行一个