49. JS 单线程模型的本质与浏览器协作机制 副本.md 来源:https://my.feishu.cn/file/DQTobScOZoDGsuxiCracMr15nPc 采集状态:已完成正文采集;原文件源码视图逐行采集,行号 1 至 89 连续,滚动到底。 ### 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) (官方规范) 原文链接与附件索引 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