微信做网站支付工具厦门网站建设外包
2026/6/12 8:16:44 网站建设 项目流程
微信做网站支付工具,厦门网站建设外包,有什么网站开发客户,网络营销的方式你以为JavaScript是单线程的#xff0c;但它却用事件循环实现了伪异步。理解宏任务和微任务#xff0c;是掌握现代前端异步编程的关键。引言#xff1a;从一道经典面试题说起javascriptconsole.log(1);setTimeout(() {console.log(2); }, 0);Promise.resol…你以为JavaScript是单线程的但它却用事件循环实现了伪异步。理解宏任务和微任务是掌握现代前端异步编程的关键。引言从一道经典面试题说起javascriptconsole.log(1); setTimeout(() { console.log(2); }, 0); Promise.resolve().then(() { console.log(3); }); console.log(4); // 输出顺序是什么如果你的答案是1, 4, 3, 2那么恭喜你已经理解了事件循环的基本概念。但事件循环远不止于此...第一部分JavaScript运行环境的真相1.1 为什么JavaScript是单线程的JavaScript最初被设计为浏览器脚本语言主要用于处理DOM操作。多线程同时操作DOM会带来复杂的同步问题。因此JavaScript采用了单线程事件循环的模型。javascript// 浏览器中的JavaScript执行环境 ┌───────────────────────────┐ │ JavaScript │ ← 单线程执行 │ Engine (V8/SpiderMonkey)│ └───────────────────────────┘ ↑ ↓ ┌───────────────────────────┐ │ Web APIs (浏览器提供) │ ← 异步APIsetTimeout、DOM事件、Ajax等 └───────────────────────────┘ ↑ ↓ ┌───────────────────────────┐ │ Task Queue (任务队列) │ ← 待执行的回调函数 └───────────────────────────┘1.2 事件循环的基本原理javascript// 事件循环的简化模型 while (eventLoop.waitForTask()) { // 1. 从任务队列中取出一个任务 const task eventLoop.getNextTask(); // 2. 执行任务 try { task(); } catch (error) { console.error(任务执行出错:, error); } // 3. 执行所有微任务 eventLoop.processMicrotasks(); // 4. 渲染如果需要 if (shouldRender()) { eventLoop.render(); } }第二部分宏任务 vs 微任务2.1 什么是宏任务宏任务MacroTask代表一个独立的、完整的工作单元。每个宏任务执行完后浏览器可能会进行渲染。常见的宏任务script整体代码setTimeout / setIntervalsetImmediateNode.jsI/O操作UI渲染浏览器事件回调click、load等MessageChanneljavascript// 宏任务示例 console.log(脚本开始); // 这是第一个宏任务 setTimeout(() { console.log(setTimeout回调); // 新的宏任务 }, 0); button.addEventListener(click, () { console.log(按钮点击); // 事件回调是宏任务 }); // 当前宏任务结束2.2 什么是微任务微任务MicroTask是在当前宏任务结束后、下一个宏任务开始前立即执行的任务。微任务队列会在每个宏任务执行完毕后清空。常见的微任务Promise.then / .catch / .finallyasync/await本质是PromiseMutationObserver浏览器process.nextTickNode.js优先级最高queueMicrotask APIjavascript// 微任务示例 console.log(开始); Promise.resolve().then(() { console.log(Promise 1); // 微任务 }).then(() { console.log(Promise 2); // 微任务 }); queueMicrotask(() { console.log(queueMicrotask); // 微任务 }); console.log(结束); // 输出开始 → 结束 → Promise 1 → Promise 2 → queueMicrotask2.3 完整的执行顺序javascript// 完整的事件循环顺序示例 console.log(1 - 同步代码宏任务开始); setTimeout(() { console.log(2 - setTimeout宏任务); Promise.resolve().then(() { console.log(3 - 内层Promise微任务); }); }, 0); Promise.resolve().then(() { console.log(4 - 外层Promise微任务); setTimeout(() { console.log(5 - 内层setTimeout宏任务); }, 0); }); console.log(6 - 同步代码宏任务结束); // 执行顺序分析 // 1. 执行当前宏任务整体代码输出 1, 6 // 2. 执行微任务队列输出 4 // 3. 执行下一个宏任务第一个setTimeout输出 2 // 4. 执行该宏任务产生的微任务输出 3 // 5. 执行下一个宏任务第二个setTimeout输出 5第三部分浏览器与Node.js的事件循环差异3.1 浏览器的事件循环模型javascript// 浏览器事件循环阶段 ┌───────────────────────┐ │ 宏任务队列 │ │ 1. 执行一个宏任务 │ └──────────┬────────────┘ │ ┌──────────▼────────────┐ │ 微任务队列 │ │ 2. 执行所有微任务 │ └──────────┬────────────┘ │ ┌──────────▼────────────┐ │ requestAnimation │ │ 3. 执行RAF回调 │ └──────────┬────────────┘ │ ┌──────────▼────────────┐ │ 渲染阶段 │ │ 4. 样式计算、布局、绘制 │ └──────────┬────────────┘ │ ┌──────────▼────────────┐ │ requestIdleCallback │ │ 5. 执行RIC回调空闲时│ └───────────────────────┘3.2 Node.js的事件循环模型javascript// Node.js事件循环阶段更复杂 ┌───────────────────────────┐ │ timers阶段 │ ← 执行setTimeout/setInterval回调 └─────────────┬─────────────┘ │ ┌─────────────▼─────────────┐ │ pending callbacks阶段 │ ← 执行上一轮未执行的I/O回调 └─────────────┬─────────────┘ │ ┌─────────────▼─────────────┐ │ idle, prepare阶段 │ ← 内部使用 └─────────────┬─────────────┘ │ ┌─────────────▼─────────────┐ │ poll阶段 │ ← 检索新的I/O事件执行相关回调 └─────────────┬─────────────┘ │ ┌─────────────▼─────────────┐ │ check阶段 │ ← 执行setImmediate回调 └─────────────┬─────────────┘ │ ┌─────────────▼─────────────┐ │ close callbacks阶段 │ ← 执行close事件的回调 └───────────────────────────┘3.3 关键差异对比javascript// 差异1process.nextTick vs Promise Promise.resolve().then(() { console.log(Promise); }); process.nextTick(() { console.log(nextTick); // 先执行 }); // 差异2setTimeout vs setImmediate setTimeout(() { console.log(timeout); }, 0); setImmediate(() { console.log(immediate); // 顺序不确定取决于当前执行环境 }); // 差异3浏览器 vs Node.js的微任务执行时机 setTimeout(() { console.log(timeout1); Promise.resolve().then(() { console.log(promise1); }); }, 0); setTimeout(() { console.log(timeout2); Promise.resolve().then(() { console.log(promise2); }); }, 0); // 浏览器输出timeout1 → promise1 → timeout2 → promise2 // Node.js可能输出timeout1 → timeout2 → promise1 → promise2第四部分实战应用场景4.1 性能优化避免阻塞渲染javascript// 不好大量同步任务阻塞渲染 function processLargeArray(array) { const results []; for (let i 0; i array.length; i) { // 昂贵的计算 results.push(expensiveCalculation(array[i])); } return results; } // 好将任务分解为多个宏任务 function processLargeArrayAsync(array, chunkSize 100) { return new Promise((resolve) { const results []; let index 0; function processChunk() { const end Math.min(index chunkSize, array.length); for (; index end; index) { results.push(expensiveCalculation(array[index])); } if (index array.length) { // 使用setTimeout让出控制权允许渲染 setTimeout(processChunk, 0); } else { resolve(results); } } processChunk(); }); } // 更好使用微任务避免不必要的渲染 function processLargeArrayMicrotask(array, chunkSize 100) { return new Promise((resolve) { const results []; let index 0; function processChunk() { const end Math.min(index chunkSize, array.length); for (; index end; index) { results.push(expensiveCalculation(array[index])); } if (index array.length) { // 使用queueMicrotask在当前任务结束后立即执行 queueMicrotask(processChunk); } else { resolve(results); } } processChunk(); }); }4.2 实现优先级调度javascriptclass TaskScheduler { constructor() { this.microTasks []; this.macroTasks []; this.isProcessing false; } // 添加高优先级任务微任务 addMicrotask(task) { this.microTasks.push(task); this.scheduleRun(); } // 添加普通任务宏任务 addMacrotask(task) { this.macroTasks.push(task); this.scheduleRun(); } scheduleRun() { if (this.isProcessing) return; this.isProcessing true; // 使用微任务来启动处理 queueMicrotask(() { this.processTasks(); }); } processTasks() { // 先处理所有微任务 while (this.microTasks.length 0) { const task this.microTasks.shift(); try { task(); } catch (error) { console.error(微任务执行失败:, error); } } // 然后处理一个宏任务 if (this.macroTasks.length 0) { const task this.macroTasks.shift(); try { task(); } catch (error) { console.error(宏任务执行失败:, error); } } // 如果还有任务继续调度 if (this.microTasks.length 0 || this.macroTasks.length 0) { this.scheduleRun(); } else { this.isProcessing false; } } } // 使用示例 const scheduler new TaskScheduler(); scheduler.addMacrotask(() console.log(宏任务 1)); scheduler.addMicrotask(() console.log(微任务 1)); scheduler.addMacrotask(() console.log(宏任务 2)); scheduler.addMicrotask(() console.log(微任务 2)); // 输出微任务 1 → 微任务 2 → 宏任务 1 → 宏任务 24.3 实现防抖与节流的升级版javascript// 使用微任务优化的防抖 function debounceMicrotask(fn, delay) { let timerId null; let microtaskQueued false; return function(...args) { const context this; // 清除之前的定时器 if (timerId) { clearTimeout(timerId); } // 如果没有微任务在排队创建一个 if (!microtaskQueued) { microtaskQueued true; queueMicrotask(() { microtaskQueued false; // 设置新的定时器 timerId setTimeout(() { fn.apply(context, args); timerId null; }, delay); }); } }; } // 使用示例 const expensiveSearch debounceMicrotask((query) { console.log(搜索:, query); // 实际搜索逻辑 }, 300); // 快速连续输入 expensiveSearch(a); expensiveSearch(ab); expensiveSearch(abc); // 只执行最后一次4.4 React中的批量更新javascript// React利用事件循环实现状态批量更新 class FakeReact { constructor() { this.state {}; this.isBatchingUpdates false; this.pendingStates []; } setState(newState) { if (this.isBatchingUpdates) { // 如果在批处理中收集状态更新 this.pendingStates.push(newState); } else { // 否则直接更新 this.applyUpdate(newState); } } batchedUpdates(callback) { this.isBatchingUpdates true; try { callback(); } finally { this.isBatchingUpdates false; // 在微任务中执行所有收集的更新 if (this.pendingStates.length 0) { queueMicrotask(() { const states [...this.pendingStates]; this.pendingStates []; states.forEach(state { this.applyUpdate(state); }); }); } } } applyUpdate(newState) { this.state { ...this.state, ...newState }; console.log(状态更新:, this.state); } } // 使用示例 const react new FakeReact(); react.batchedUpdates(() { react.setState({ count: 1 }); react.setState({ count: 2 }); react.setState({ count: 3 }); }); // 只会触发一次更新{ count: 3 }第五部分常见陷阱与最佳实践5.1 微任务无限递归javascript// 危险的代码微任务无限循环 function dangerousMicrotaskLoop() { Promise.resolve().then(() { console.log(微任务执行); dangerousMicrotaskLoop(); // 递归调用 }); } // 这会阻塞事件循环导致页面无响应 // 因为微任务队列永远不会清空 // 安全的方式使用宏任务 function safeMacrotaskLoop() { console.log(宏任务执行); setTimeout(safeMacrotaskLoop, 0); // 允许渲染 }5.2 混合使用宏任务和微任务javascript// 不推荐的模式 button.addEventListener(click, () { // 宏任务中产生微任务 Promise.resolve().then(() { // 微任务中又产生宏任务 setTimeout(() { // 难以追踪执行顺序 console.log(多层嵌套); }, 0); }); }); // 推荐的模式保持清晰的任务层次 async function handleClick() { // 步骤1微任务处理 await processImmediate(); // 步骤2宏任务处理 setTimeout(() { processDelayed(); }, 0); } button.addEventListener(click, handleClick);5.3 最佳实践总结优先使用微任务对于需要立即执行但不阻塞渲染的任务适时使用宏任务对于可以延迟执行或需要允许渲染的任务避免微任务递归防止微任务队列永不空合理使用async/await理解其基于Promise微任务的本质考虑使用queueMicrotask比Promise.resolve().then()更语义化注意执行顺序在混合使用时要清晰了解执行顺序第六部分现代API与事件循环6.1 requestAnimationFramejavascript// requestAnimationFrame在渲染前执行 console.log(开始); setTimeout(() { console.log(setTimeout); }, 0); requestAnimationFrame(() { console.log(requestAnimationFrame); }); Promise.resolve().then(() { console.log(Promise); }); console.log(结束); // 典型输出开始 → 结束 → Promise → requestAnimationFrame → setTimeout // 但注意RAF在渲染前执行时机可能因浏览器而异6.2 requestIdleCallbackjavascript// 在空闲时间执行低优先级任务 function processIdleTasks(deadline) { while (tasks.length 0 deadline.timeRemaining() 0) { const task tasks.shift(); task(); } if (tasks.length 0) { requestIdleCallback(processIdleTasks); } } // 与事件循环的配合 button.addEventListener(click, () { // 高优先级任务立即执行 console.log(点击处理); // 低优先级任务在空闲时执行 requestIdleCallback(() { console.log(空闲任务); }); });6.3 MutationObserverjavascript// MutationObserver使用微任务 const observer new MutationObserver((mutations) { console.log(DOM变化, mutations); }); observer.observe(document.body, { childList: true, subtree: true }); // 测试 setTimeout(() { document.body.appendChild(document.createElement(div)); console.log(添加元素后); }, 0); // 输出顺序添加元素后 → DOM变化 // MutationObserver回调作为微任务执行总结掌握事件循环的艺术事件循环是JavaScript异步编程的核心机制理解宏任务和微任务的差异对于编写高性能、响应迅速的前端应用至关重要。关键要点宏任务是独立的执行完一个宏任务后会执行所有微任务微任务是紧接的在当前宏任务结束后立即执行渲染时机通常在微任务执行完毕后下一个宏任务开始前优先级同步代码 微任务 渲染 宏任务何时使用什么微任务需要立即执行的状态更新、Promise处理、数据同步宏任务需要延迟执行的任务、I/O操作、用户交互处理requestAnimationFrame与渲染相关的动画、视觉更新requestIdleCallback低优先级的后台任务记住事件循环不是JavaScript引擎的特性而是宿主环境浏览器/Node.js提供的机制。不同的宿主环境可能有不同的实现但核心概念相通。通过深入理解事件循环你不仅能写出更好的异步代码还能更有效地调试性能问题构建更流畅的用户体验。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询