1. 核心概念 (Core Concept)
JavaScript 的单线程模型指的是:运行 JavaScript 代码的线程只有一个,同一时间只能执行一个任务。
2. 为什么需要它? (The "Why")
- 简化编程模型: 单线程避免了多线程中最常见的同步问题(如死锁、数据竞争),使得程序逻辑更易于理解和编写。
- 适应浏览器环境: 浏览器最初设计时,DOM 的操作是严格同步的,单线程模型可以避免复杂的同步机制来处理 DOM 的修改,确保 DOM 状态的可见性和一致性。例如,如果两个线程同时尝试修改同一个 DOM 元素,会造成冲突和不确定性。
3. API 与用法 (API & Usage)
由于单线程,JavaScript 的执行是线性的。为了处理耗时操作(如网络请求、定时器、事件处理),浏览器引入了异步机制。这主要依赖以下概念:
- 任务队列 (Task Queue / Callback Queue): 用于存放异步操作执行完成后的回调函数。
- 事件循环 (Event Loop): 不停地检查主线程中的调用栈是否为空,如果为空,则从任务队列中取出待执行的回调函数放入调用栈,使其得以执行。
- 宏任务 (Macrotasks): 宏任务包括 script (整体代码)、
setTimeout、setInterval、setImmediate(Node.js)、I/O、UI Rendering。 - 微任务 (Microtasks): 微任务包括
process.nextTick(Node.js)、Promise.then()、MutationObserver。
事件循环的执行顺序大致为:
- 执行当前宏任务(比如整个 script 代码块)。
- 执行过程中遇到的同步任务立即执行,异步任务的回调放入对应的任务队列。
- 当前宏任务执行完毕。
- 检查微任务队列,执行所有可用的微任务(直到微任务队列为空)。
- 执行渲染(如果需要)。
- 开始下一个宏任务(通常是从宏任务队列中取出一个)。
这是一个基础的事件循环示例概念代码(非真实的浏览器内部实现代码):
javascript
// 简化概念代码,非真实引擎实现
function eventLoop() {
while (true) {
// 1. 执行一个宏任务 (例如,从任务队列取出最早的)
let task = getNextMacrotaskFromQueue();
if (task) {
executeTask(task);
// 2. 执行所有微任务
while (microtaskQueueIsNotEmpty()) {
let microtask = getNextMicrotaskFromQueue();
executeTask(microtask);
}
// 3. 渲染 (如果需要)
if (shouldRender()) {
render();
}
} else {
// 没有宏任务了,可以休息一下或者等待新任务
waitForNewTask();
}
}
}实际开发中,我们通过使用异步 API 来利用浏览器的协作机制:
javascript
console.log('Start'); // 同步任务
setTimeout(function() {
console.log('setTimeout callback'); // 宏任务回调,进入任务队列
}, 0); // 即使延迟为0,它仍然是宏任务
Promise.resolve().then(function() {
console.log('Promise then'); // 微任务回调,进入微任务队列
});
console.log('End'); // 同步任务
// 输出顺序: Start -> End -> Promise then -> setTimeout callback4. 关键注意事项 (Key Considerations)
- 长时间运行的同步代码会阻塞: 如果主线程中的同步代码执行时间过长(例如,复杂的计算或死循环),会阻塞事件循环,导致页面无响应、无法处理用户输入和定时器等到期。
- 异步不等于多线程: JavaScript 的异步是通过事件循环和回调机制实现的单线程内的非阻塞行为,而非创建新的线程来并行执行任务。
- Node.js 环境的差异: Node.js 在事件循环和任务队列处理上与浏览器环境有些差异,例如引入了
process.nextTick(更高优先级的微任务)和setImmediate(特殊阶段的宏任务)。 - Web Workers: 浏览器提供了 Web Workers API,允许 JavaScript 创建独立的后台线程执行计算密集型任务,但 Web Workers 不能直接访问 DOM。这是一种在单线程模型之外进行并行计算的方式。
