单线程与异步的关系

理解 JavaScript 单线程模型和异步机制的配合原理

问题

什么是单线程,和异步的关系?

解答

什么是单线程

单线程意味着 JavaScript 同一时间只能执行一个任务。所有代码在一个主线程上按顺序执行。

// 单线程执行:按顺序一行一行执行
console.log('1');
console.log('2');
console.log('3');
// 输出:1 2 3

为什么需要异步

如果所有操作都同步执行,遇到耗时任务(网络请求、定时器)会阻塞后续代码:

// 假设这是同步请求(实际会阻塞)
const data = syncRequest('/api/data'); // 等待 3 秒
console.log('继续执行'); // 3 秒后才执行,页面卡死

单线程如何实现异步

JavaScript 通过**事件循环(Event Loop)**实现异步。耗时任务交给浏览器其他线程处理,完成后将回调放入任务队列,主线程空闲时再执行。

console.log('1'); // 同步,立即执行

setTimeout(() => {
  console.log('2'); // 异步,放入任务队列
}, 0);

Promise.resolve().then(() => {
  console.log('3'); // 异步,放入微任务队列
});

console.log('4'); // 同步,立即执行

// 输出顺序:1 4 3 2

执行流程图解

// 主线程执行栈
// ┌─────────────────┐
// │  console.log(1) │ → 执行,输出 1
// │  setTimeout     │ → 交给定时器线程,回调放入宏任务队列
// │  Promise.then   │ → 回调放入微任务队列
// │  console.log(4) │ → 执行,输出 4
// └─────────────────┘
//          ↓
// 执行栈清空,检查微任务队列
//          ↓
// ┌─────────────────┐
// │  console.log(3) │ → 执行,输出 3
// └─────────────────┘
//          ↓
// 微任务清空,检查宏任务队列
//          ↓
// ┌─────────────────┐
// │  console.log(2) │ → 执行,输出 2
// └─────────────────┘

完整示例

console.log('script start');

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

new Promise((resolve) => {
  console.log('promise constructor'); // 同步执行
  resolve();
}).then(() => {
  console.log('promise then');
});

console.log('script end');

// 输出顺序:
// script start
// promise constructor
// script end
// promise then
// setTimeout

关键点

  • 单线程:JS 主线程同一时间只能执行一个任务
  • 异步不是多线程:异步任务由浏览器其他线程处理(定时器线程、网络线程),但回调仍在主线程执行
  • 事件循环:主线程执行完同步代码后,从任务队列取出回调执行
  • 微任务优先:Promise 回调(微任务)优先于 setTimeout 回调(宏任务)
  • 不阻塞:异步让单线程能处理耗时操作而不卡死页面