跳转到内容
主菜单
主菜单
移至侧栏
隐藏
导航
首页
最近更改
随机页面
MediaWiki帮助
代码酷
搜索
搜索
中文(中国大陆)
外观
创建账号
登录
个人工具
创建账号
登录
未登录编辑者的页面
了解详情
贡献
讨论
编辑“︁
事件循环机制
”︁
页面
讨论
大陆简体
阅读
编辑
编辑源代码
查看历史
工具
工具
移至侧栏
隐藏
操作
阅读
编辑
编辑源代码
查看历史
常规
链入页面
相关更改
特殊页面
页面信息
外观
移至侧栏
隐藏
您的更改会在有权核准的用户核准后向读者展示。
警告:
您没有登录。如果您进行任何编辑,您的IP地址会公开展示。如果您
登录
或
创建账号
,您的编辑会以您的用户名署名,此外还有其他益处。
反垃圾检查。
不要
加入这个!
{{Note|本文适合对JavaScript有一定基础的读者阅读,但初学者也能通过逐步学习理解事件循环机制的核心概念。}} = 事件循环机制 = '''事件循环机制'''(Event Loop)是JavaScript实现异步编程的核心模型,它决定了代码的执行顺序和任务的调度方式。理解事件循环对于编写高效、无阻塞的JavaScript代码至关重要。 == 概述 == JavaScript是单线程语言,意味着它一次只能执行一个任务。为了处理异步操作(如定时器、网络请求、用户交互等),JavaScript使用'''事件循环'''来协调任务执行顺序。事件循环由以下关键部分组成: * '''调用栈(Call Stack)''':存储同步任务的执行上下文 * '''任务队列(Task Queue)''':存储待处理的异步回调 * '''微任务队列(Microtask Queue)''':存储优先级更高的异步回调 * '''事件循环线程''':负责监控调用栈和队列 == 工作原理 == 事件循环的基本流程可以用以下伪代码表示: <syntaxhighlight lang="javascript"> while (true) { // 1. 执行调用栈中的任务 executeCallStack(); // 2. 检查并执行所有微任务 while (microtaskQueue.length > 0) { executeMicrotask(); } // 3. 执行一个宏任务 if (taskQueue.length > 0) { executeTask(); } // 4. 必要时进行渲染更新 if (needsRendering()) { updateRendering(); } } </syntaxhighlight> === 执行顺序规则 === 1. 同步代码优先执行(调用栈) 2. 当调用栈清空后,检查并执行所有微任务(如Promise回调) 3. 执行一个宏任务(如setTimeout回调) 4. 重复该循环 <mermaid> graph TD A[同步代码] --> B[调用栈空?] B -->|是| C[执行所有微任务] C --> D[执行一个宏任务] D --> B B -->|否| E[继续执行同步代码] </mermaid> == 代码示例 == === 基础示例 === <syntaxhighlight lang="javascript"> console.log('1. 同步代码开始'); setTimeout(() => { console.log('4. 宏任务执行'); }, 0); Promise.resolve().then(() => { console.log('3. 微任务执行'); }); console.log('2. 同步代码结束'); </syntaxhighlight> 输出结果: <pre> 1. 同步代码开始 2. 同步代码结束 3. 微任务执行 4. 宏任务执行 </pre> === 复杂示例 === <syntaxhighlight lang="javascript"> console.log('脚本开始'); setTimeout(() => { console.log('setTimeout1'); Promise.resolve().then(() => console.log('Promise in setTimeout1')); }, 0); setTimeout(() => { console.log('setTimeout2'); }, 0); Promise.resolve().then(() => { console.log('Promise1'); return Promise.resolve(); }).then(() => { console.log('Promise2'); }); console.log('脚本结束'); </syntaxhighlight> 可能的输出结果: <pre> 脚本开始 脚本结束 Promise1 Promise2 setTimeout1 Promise in setTimeout1 setTimeout2 </pre> == 任务类型分类 == === 宏任务(Macrotasks) === * <code>setTimeout</code>/<code>setInterval</code> * I/O操作 * UI渲染 * <code>setImmediate</code>(Node.js) * <code>requestAnimationFrame</code>(浏览器) === 微任务(Microtasks) === * <code>Promise.then/catch/finally</code> * <code>MutationObserver</code> * <code>process.nextTick</code>(Node.js) * <code>queueMicrotask</code> == 浏览器与Node.js差异 == {| class="wikitable" |- ! 环境 !! 特点 |- | 浏览器 || 每帧执行一次事件循环,包含渲染步骤 |- | Node.js || 使用libuv实现,有多个阶段(timers、poll、check等) |} == 实际应用场景 == === 性能优化 === 将耗时任务分解为多个微任务,避免阻塞UI: <syntaxhighlight lang="javascript"> function processLargeData(data) { // 分批处理大数据 const chunkSize = 1000; let i = 0; function processChunk() { const chunk = data.slice(i, i + chunkSize); // 处理数据块... i += chunkSize; if (i < data.length) { // 使用微任务继续处理 Promise.resolve().then(processChunk); } } processChunk(); } </syntaxhighlight> === 动画调度 === 结合<code>requestAnimationFrame</code>和事件循环实现流畅动画: <syntaxhighlight lang="javascript"> function animate() { // 动画逻辑... requestAnimationFrame(animate); } animate(); </syntaxhighlight> == 常见问题 == === 为什么Promise比setTimeout先执行? === 因为Promise回调属于微任务,而setTimeout回调属于宏任务。事件循环总是先清空微任务队列才会处理宏任务。 === 如何避免回调地狱? === 使用Promise/async-await可以更清晰地表达异步流程: <syntaxhighlight lang="javascript"> async function fetchData() { try { const res1 = await fetch('/api/1'); const data1 = await res1.json(); const res2 = await fetch(`/api/2?id=${data1.id}`); return await res2.json(); } catch (error) { console.error('请求失败:', error); } } </syntaxhighlight> == 数学表示 == 事件循环的执行顺序可以用以下公式表示: <math> ExecutionOrder = \underbrace{Sync}_{\text{同步代码}} \rightarrow \underbrace{\sum Microtasks}_{\text{所有微任务}} \rightarrow \underbrace{1 Macrotask}_{\text{一个宏任务}} \rightarrow \text{循环} </math> == 总结 == * JavaScript通过事件循环实现异步执行 * 任务分为同步任务、微任务和宏任务 * 执行顺序:同步 → 所有微任务 → 一个宏任务 → 循环 * 合理利用事件循环机制可以优化性能,避免UI阻塞 {{Tip|使用Chrome DevTools的Performance面板可以可视化观察事件循环的执行过程。}} [[Category:前端基础]] [[Category:JavaScript]] [[Category:计算机科学]] [[Category:面试技巧]]
摘要:
请注意,所有对代码酷的贡献均被视为依照知识共享署名-非商业性使用-相同方式共享发表(详情请见
代码酷:著作权
)。如果您不希望您的文字作品被随意编辑和分发传播,请不要在此提交。
您同时也向我们承诺,您提交的内容为您自己所创作,或是复制自公共领域或类似自由来源。
未经许可,请勿提交受著作权保护的作品!
取消
编辑帮助
(在新窗口中打开)
该页面使用的模板:
模板:Note
(
编辑
)
模板:Tip
(
编辑
)