新闻详情

新闻详情

首页 / 资讯中心 / 详情

前端精读:深入 JavaScript 事件循环(Event Loop)与异步编程原理

发布时间:2026/10/1 17:02:10来源:尧图网络
前端精读:深入 JavaScript 事件循环(Event Loop)与异步编程原理
文档技术博客教程【免费下载链接】weekly前端精读周刊。帮你理解最前沿、实用的技术。项目地址https://gitcode.com/GitHub_Trending/we/weekly点击查看免费下载事件循环Event Loop是 JavaScript 运行机制的核心内科它决定了setTimeout、Promise、fetch这些异步代码究竟在什么时机、以什么顺序被执行。本文以本期周刊精读的《Javascript 事件循环与异步》为骨架结合仓库内 Tasks, microtasks 精读、setState 原理精读 等系列文章帮你彻底打通 Call Stack、宿主环境、Microtask/Macrotask 之间的关系并给出一个 React 生命周期中可复现的异步setState实验读完即可在实际项目中写出更可靠、可预期的异步代码。1 引言为什么事件循环像一门内科本期精读的原始文章出自 sessionstack 的 How JavaScript works 系列该系列深入探讨 JS 内部原理并指出只有深入了解 JS 的工作方式才可能写出更好的代码。事件循环之于 JavaScript 开发者就像内科之于医生我们平时只关注外科创伤表层 API 用法却忽视内科问题——setTimeout、Promise这些基础概念如果只停留在浮层理解代码往往会在意想不到的地方出问题。对前端新人来说事件循环也几乎是面试的必考基础题。本文不展开原文的 Promise、async/await 部分聚焦 Event Loop 本身它如何与 Call Stack、宿主环境协作以及 Microtask 与 Macrotask 两种异步队列的差异。2 Event Loop 与 Call Stack、Web APIs 之间的关系2.1 三个角色各司其职在浏览器与 Node.js 中一次 JS 代码的执行涉及三个关键角色Call Stack调用栈所有同步代码的执行场所遵循后进先出LIFO规则。函数调用时入栈函数返回时出栈。Event Loop事件循环本篇文章的主角负责监控调用栈与异步队列在合适时机把异步回调推回调用栈执行。Web APIs宿主环境泛指浏览器提供的 APIDOM、fetch、定时器等或 Node.js 中的底层 C 能力。异步时机的判定由宿主环境负责而不由 JS 引擎负责。原文档用 16 张图演示了 5 行代码的完整执行过程核心结论可以概括为一句话任何同步代码只存在于 Call Stack 中只有异步代码不一定是回调才会进入 Event Loop 的队列。2.2 哪些代码属于异步典型的异步代码包括setTimeout() setInterval() Promise.resolve().then() fetch().then()这些异步代码在执行时不会直接进入 Call Stack而是进入 Event Loop 队列。当 JS 主线程Call Stack执行完毕、且异步时机已到Event Loop 才会把异步回调中的代码推入 Call Stack 执行。2.3 执行时机由宿主环境决定一个容易忽略的关键点异步代码何时开始执行是由宿主环境决定的而不是 JS 引擎。原因是当调用栈清空后JS 引擎本身不会再主动执行任何代码除非 Event Loop 队列中有内容被推送回 Call Stack。以fetch为例JS 调用浏览器发送请求后直到浏览器主动通知 JS请求已完成之前JS 无法干预任何环节——网络 IO 的调度完全在宿主环境浏览器/Node.js内部完成。这也解释了为什么setTimeout(fn, 0)并不是立即执行它只是把回调交给宿主环境的定时器线程计时倒计时结束后再把回调送入事件循环队列等待调用栈空闲。3 Microtask 与 Macrotask两种异步队列3.1 一维队列是不够的事件循环处理异步的方式分两种Macrotask宏任务setTimeout、setInterval之流。Microtask微任务Promise之流。原文档给出了一个非常直观的模型异步队列是周而复始循环执行的可以看作一个二维数组——横排是当前队列中的每一个函数纵排是每一个队列。Macrotask的方式是将执行函数添加到新的纵排新开一个队列Microtask将执行函数添加到当前执行到队列的横排插入到当前队列尾部。因此Microtask的插入是轻量的能最快被执行到——它不需要等待整条宏任务队列轮转只要当前宏任务结束后即可立刻消费。3.2 用真实输出验证执行顺序仓库内 Tasks, microtasks, queues and schedules 精读 给出了一个经典验证用例console.log(script start); setTimeout(function () { console.log(setTimeout); }, 0); Promise.resolve() .then(function () { console.log(promise1); }) .then(function () { console.log(promise2); }); console.log(script end);正确输出顺序是script start script end promise1 promise2 setTimeout原因如下同步脚本执行优先级最高script start、script end先后打印Promise的回调进入MicrotaskssetTimeout的回调进入Tasks一次事件循环中Microtasks 优先于 Tasks 执行Microtasks 执行期间新插入的 Microtaskpromise2会继续按顺序消费直到队列清空才会轮到 Tasks 中的setTimeout。更精细地看一个事件循环内的大致流程是执行同步代码 → 调用栈清空 → 清空 Microtasks 队列 → 浏览器可能执行渲染 → 执行下一个 Task。需要注意这里讨论的是一次 Event Loop 内立即执行的优先级与定时器的延迟时间如 3000ms、0ms不要混淆。4 实战精读componentWillMount 中异步 setState 的时灵时不灵理解了事件循环才能真正看懂 React 生命周期中那些看似诡异的行为。原文档作者在编写 dob-react 测试时发现componentWillMount函数在Microtask时机调用setState不会触发 rerender。4.1 只 render 一次的错误写法class Hello extends React.Component { async componentWillMount() { await immediate(() { this.setState({ a: 1 }); }); } render() { /**/ } }配套的immediate函数如下function immediate(fn) { return new Promise(resolve { fn(); resolve(); }); }这种写法下组件只会 render 一次。4.2 render 两次的正确写法如果再套一层Promise.resolve().then()让fn真正推迟到微任务时机执行function immediate(fn) { return new Promise(resolve Promise.resolve().then(() { fn(); resolve(); }) ); }此时组件会render 两次。4.3 原理与勘误原文档的 ps 补充非常重要第一种immediate写法其实是错误的——fn()在new Promise的 executor 中同步执行了await并没有真正推迟到微任务时机所以setState仍然发生在 React 生命周期内的同步渲染过程中setState被合并只触发一次 render。正确做法应使用Promise.resolve().then()将fn的调用真正推迟到 Microtask 时机。这个实验暴露了一个普遍问题在生命周期函数中异步setState的合并机制时而生效、时而不生效取决于setState被调用的时机落在同步执行阶段还是微任务阶段。这正是事件循环知识在真实业务中的价值——只有理解了 Microtask 的消费时机才能解释 React 为何合并状态更新以及何时合并会失效。4.4 从 React 实现看 setState 的调度入口要彻底理解上述现象可以顺藤摸瓜看 React 的状态更新链路。setState 做了什么精读 指出react包本身不包含 DOM 更新逻辑它只通过updater接口向渲染器react-dom、react-native等反向通信// A bit simplified setState(partialState, callback) { // Use the updater field to talk back to the renderer! this.updater.enqueueSetState(this, partialState, callback); }也就是说setState是否合并、何时触发 rerender最终由react-dom的渲染器在事件循环的某个时机同步任务还是微任务决定——这与本文事件循环的分析天然衔接。5 从事件循环到 React 调度与 Promise 工程实践事件循环并非孤立的面试知识点它向下支撑了框架的调度设计向上决定了业务异步代码的写法。5.1 React 的调度本质是事件循环上的时间分片Scheduling in React 精读 指出浏览器同一时间只能做一件事肉眼可识别的刷新频率约 60FPS意味着渲染、动画、响应用户输入必须在约 16ms 内完成。React 16 的 Concurrent 模式把同步渲染拆解为可中断的异步渲染利用浏览器事件循环的空闲分片分批执行任务本质上是站在事件循环之上做任务优先级调度。5.2 Promise 串行队列的底层原理用 Reduce 实现 Promise 串行执行精读 从另一个角度印证了事件循环模型reduce是同步执行的在一个事件循环内完成——它只是在内存中快速构造了一条 Promise 执行链function runPromiseByQueue(myPromises) { myPromises.reduce( (previousPromise, nextPromise) previousPromise.then(() nextPromise()), Promise.resolve() ); }reduce的作用就是把每个 Promise 完成后执行下一个的冗余队列代码折叠成一行生成。更现代的做法是用async/await改写为顺序等待async function runPromiseByQueue(myPromises) { for (let value of myPromises) { await value(); } }理解了两者的差异前者同步构造队列、后者异步逐项等待才能回答为什么Promise.all是并行、而await逐个写是串行这类问题。6 总结理解事件循环只是第一步。原文档作者坦言想写好稳健的业务代码需要三层能力理解内科知识Call Stack 只装同步代码异步代码进入 Event Loop 队列Microtask 优先于 Macrotask 消费读懂框架源码例如 React 的setState通过updater委托给渲染器实现调度与合并行为由渲染器在事件循环的时机决定保证不会忘异步时序知识是用到才想起来型知识建议通过类似本文第 4 节的可复现实验持续巩固。最后回到实践层面仓库中 Tasks, microtasks 精读 给出了两条务实建议业务逻辑不要巧妙依赖 Microtask 与 Task 执行顺序的微妙差异——不同浏览器Chrome、Firefox、Safari、Edge对任务顺序的实现存在差异依赖顺序的代码非常脆弱不要死记硬背调用顺序——只要记住核心规则即可推导一次事件循环中同步代码优先随后清空 Microtasks再处理 TasksMicrotasks 执行期间新插入的 Microtasks 会按序继续执行。类似的异步思维贯穿仓库多个主题处理异步异常的 捕获所有异步 error、并发场景下 async/await 是把双刃剑 中对顺序 await 拖慢并发的警示都可与本文的事件循环模型互相印证。当你下次面对为什么结果顺序不对为什么 setState 没生效时不妨先回到事件循环这个内科诊断一遍。赞分享文档技术博客教程【免费下载链接】weekly前端精读周刊。帮你理解最前沿、实用的技术。项目地址https://gitcode.com/GitHub_Trending/we/weekly点击查看免费下载相关推荐把静态图片变成动态视频一文搞懂 ComfyUI-WanVideoWrapper把静态图片变成动态视频一文搞懂 ComfyUI WanVideoWrapper 第一次做 AI 视频时我的目标很具体手上有一张红衣男子的照片想让它转头、人工智能大模型媒体生成JavaScript事件循环终极指南深入理解异步编程运行机制JavaScript事件循环终极指南深入理解异步编程运行机制 JavaScript事件循环是理解现代JavaScript异步编程的关键概念。作为一名JavaS教程示例工程JavaScript事件循环终极指南深入理解异步编程运行时机制JavaScript事件循环终极指南深入理解异步编程运行时机制 你是否曾经好奇为什么 setTimeout 并不总是准时执行为什么 Promise 比回调更文档教程上一篇ncmdump使用手记网易云NCM格式转换从拖拽到自动批量一篇讲透下一篇3步搞定NCM格式转换用ncmdump让加密音乐跨设备自由播放创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
网站建设高端定制企业官网
RELATED

相关资讯

更多精彩内容,欢迎继续阅读

较早相关资讯

最新相关资讯

Windows连Linux远程管理:SSH清理磁盘与安全关机实战 2026/10/1 17:51:42

Windows连Linux远程管理:SSH清理磁盘与安全关机实战

同学电脑卡成PPT,风扇转得跟直升机一样,系统提示磁盘空间不足,人又在图书馆回不来。这种时候如果你会Windows连Linux,直接在自己电脑上敲几行命令就能帮她把系统盘清干净、临时文件删掉、日志缩一缩,最后还可以定时关机…

阅读更多 →
算力主权实战指南:从精度体系到算力调度的工程路径 2026/10/1 17:51:41

算力主权实战指南:从精度体系到算力调度的工程路径

算力主权这件事,比大多数人想的更现实 很多人看到“全球算力主权宪章(GCCS)”这个名号,第一反应是又一份高大上的倡议书。但真在数据中心、智算集群、大模型训练一线泡过的人,会明白这东西背后全是真金白银的技术问题&…

阅读更多 →
链表核心操作深度拆解:插入、逆序、双链表与多种语言实现 2026/10/1 17:51:41

链表核心操作深度拆解:插入、逆序、双链表与多种语言实现

线性表讲到链表这一层,算是数据结构里第一道真正意义上的"坎"。很多人在 part 1 已经把单链表的结点骨架和头插法建表跑通了,但一到指定位置插入、链表逆置、带头结点与不带头结点的切换,或者从 C 语言换到 Python 重新实现一遍&am…

阅读更多 →
SAP特殊库存T详解:跨公司STO在途库存原理、配置与实战排查 2026/10/1 17:51:41

SAP特殊库存T详解:跨公司STO在途库存原理、配置与实战排查

前阵子帮客户排查一笔跨月差异,两个工厂之间货已经发出去了,但月底报表上怎么都找不出这笔库存到底挂在谁头上。后来顾问同事提醒了一句:看看特殊库存 T。结果一查 EBEW 表,问题当场就清楚了。从那以后我对 T 库存就有了一种“平时…

阅读更多 →
Stats:免费轻量的 macOS 菜单栏监控工具,盯住 Mac 健康状态 2026/10/1 17:51:41

Stats:免费轻量的 macOS 菜单栏监控工具,盯住 Mac 健康状态

Stats:免费轻量的 macOS 菜单栏监控工具,盯住 Mac 健康状态 【免费下载链接】stats macOS system monitor in your menu bar 项目地址: https://gitcode.com/GitHub_Trending/st/stats 上传进度条突然变慢,却说不清是网络的事还是机器…

阅读更多 →
YOLO舰船目标检测实战:从数据标注到部署避坑全解析 2026/10/1 17:51:35

YOLO舰船目标检测实战:从数据标注到部署避坑全解析

简介:这份资源面向深度学习与计算机视觉方向的学习者和研究者,提供基于YOLO算法的舰船目标检测完整实现方案,可用于海上救援、军事侦察与交通管理等场景的自动船只识别研究。压缩包共60个文件,约2.33MB,包含55张jpg舰船…

阅读更多 →

今日资讯

本周资讯

本月资讯

看完文章仍有疑问?

联系尧图顾问,获取一对一建站咨询

立即免费咨询 📞 400-888-8888
📞 ✉