

### 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`。

事件循环的执行顺序大致为：

1.  执行当前宏任务（比如整个 script 代码块）。
2.  执行过程中遇到的同步任务立即执行，异步任务的回调放入对应的任务队列。
3.  当前宏任务执行完毕。
4.  检查微任务队列，执行所有可用的微任务（直到微任务队列为空）。
5.  执行渲染（如果需要）。
6.  开始下一个宏任务（通常是从宏任务队列中取出一个）。

这是一个基础的事件循环示例概念代码（非真实的浏览器内部实现代码）：

```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 callback
```

### 4. 关键注意事项 (Key Considerations)

*   **长时间运行的同步代码会阻塞:** 如果主线程中的同步代码执行时间过长（例如，复杂的计算或死循环），会阻塞事件循环，导致页面无响应、无法处理用户输入和定时器等到期。
*   **异步不等于多线程:** JavaScript 的异步是通过事件循环和回调机制实现的单线程内的非阻塞行为，而非创建新的线程来并行执行任务。
*   **Node.js 环境的差异:** Node.js 在事件循环和任务队列处理上与浏览器环境有些差异，例如引入了 `process.nextTick`（更高优先级的微任务）和 `setImmediate`（特殊阶段的宏任务）。
*   **Web Workers:** 浏览器提供了 Web Workers API，允许 JavaScript 创建独立的后台线程执行计算密集型任务，但 Web Workers 不能直接访问 DOM。这是一种在单线程模型之外进行并行计算的方式。

### 5. 参考资料 (References)

*   [MDN Web Docs: Concurrency model and Event Loop](https://developer.mozilla.org/en-US/docs/Web/JavaScript/EventLoop)
*   [What the heck is the event loop anyway? | Philip Roberts | JSConf EU](https://www.youtube.com/watch?v=8aGhZQkoFbQ)
*   [Tasks, microtasks, queues and schedules - Jake Archibald](https://jakearchibald.com/2015/tasks-microtasks-queues-and-schedules/)
*   [HTML Living Standard - 8.1.6 Event loops](https://html.spec.whatwg.org/multipage/webappapis.html#event-loops) (官方规范)