当前位置: 首页 > news >正文

一个 setTimeout 引出了事件循环问题,这个事件循环到底是个啥?

昨天,在使用 Browser-Bridge 的时候,我发现每次执行浏览器指令时都是数据返回后还会等几秒才结束命令行。我就奇怪了,为什么别的 shell 命令执行时都是数据返回之后立马结束,而 bridge 命令会有明显的延迟?

好吧,让 Coding Agent 先看看,修复然后给我一个结论。

输入之后,我就去活动活动,舒展筋骨,喝了口水。

等我回到工位,我发现问题修复了。仔细一看,代码就改了一行,但总结却说了一大堆什么的,唯一让我印象深的是“事件循环”。

function waitForOpen () => { // 每 50ms 轮询一次 readyState const check = setInterval(() => { if (readyState === OPEN) { clearInterval(check); clearTimeout(timeout); // 修复后才有 resolve(); } }, 50); // 5 秒后认为连接失败 const timeout = setTimeout(() => { clearInterval(check); reject(new Error('Connection timeout')); }, 5000); } using client = new ManagedClient(options.server); // ← 块作用域开始 await client.waitForOpen();

我想着“这是啥?之前 python 项目里面似乎也有”看来这个概念是逃不掉的,得学~~

神秘的事件循环就是一个死循环

开始还以为是什么特别复杂的概念。一搜,发现这个的意思是在说执行代码的时候是单线程死循环,不停地执行任务队列中的任务。

拆成两部分就好理解了。事件指代的是待处理的任务(定时器、I/O、交互事件等等几乎所有方法调用都可以认为是事件),循环就是死循环~

或者,你把它理解为一个生产-消费者模型。事件循环指的就是用 while(true) 这样的方式不停地处理所有的消费者事件。

那回到刚刚的问题,这代码怎么就导致事件循环不能正常处理了呢?或者,为什么会延迟几秒呢?

// 每 50ms 轮询一次 readyState const check = setInterval(() => { if (readyState === OPEN) { clearInterval(check); clearTimeout(timeout); // 修复后才有 resolve(); } }, 50);

setInterval 里面在修复之前时没有 clearTimeout 的,所以主循环就会等待5s,超时之后才会继续执行,所以 Agent 跟我是事件循环导致有5s延迟,实际上是因为 setTimeout 泄露了。

这 Agent 把这理解的高度整的也太高了,直接整到了底层线程执行模型了!

但这,气氛都烘到这儿了,我突然又开始好奇了。为什么 Java 可以多线程执行?我在 JavaScript 和 Python 中能看到这样的语法,但底层都是事件循环,并不是多并发?

点击链接查看和 Kimi 的对话 Kimi | 循环Reactor

有兴趣的看看我和Kimi的对话,这里我简短点儿讲就是因为动态类型的语言,动态语言没法确定内存里面每个引用其后对象的大小。因为这一秒这个对象是 int,下一秒变 dict,内存不能预测,这导致无法做精确的逃逸分析、锁省略、无锁数据结构——编译器不知道指针会逃向何方,所以,只能放弃多线程。

难道经常说后端不能使用动态类型的语言是因为这个性能问题?

单纯从事件循环的CPU利用效率来看,其实它是高于多线程的。为什么很少人拿它做一些大规模的后端(基础设施)服务?

关键问题我觉得有两个。

一是响应不稳定。遇到高并发之后,如果某任务中需要使用较多的CPU,那后续任务就会被阻塞。

二是硬件资源浪费。现在的CPU都是多核,如果一个64核的CPU只能用一个核,那这相当于在花钱打水漂。

那为什么又适合前端呢?

我能想到的一种合理的解释就是,DOM并发操作的问题。最初的设计者也许是发现并发操作(渲染)DOM会有大量的锁冲突,还不如单线程提升对单核CPU的利用率。

--附录

点击链接查看和 Kimi 的对话 Kimi | 循环Reactor

Browser-Bridge: https://github.com/dkisser/browser-bridge

http://www.jsqmd.com/news/1076586/

相关文章:

  • 测试工程师必须要掌握的linux命令大全
  • 一份写给未来的微信机器人开发教程:如何构建大模型友好的语义网络?
  • Playwright测试自动化工具:架构优势、实战对比与最佳实践
  • AutoCAD 2027
  • 球幕投影设计内容适配球型曲面技巧​
  • 从代码逻辑到大模型心智:个人微信机器人接口的“对齐”之路
  • Variance in Adversarial Attack for Customized Diffusion Models
  • 【JAVA毕设源码分享】基于Javaweb求知资讯网的设计与实现(程序+文档+代码讲解+一条龙定制)
  • 【考研】2026/6/24
  • 3步解决Jellyfin中文刮削难题:MetaShark插件配置全攻略
  • Linux进阶--系统备份、恢复与可视化管理工具webmin、bt宝塔
  • 深度解析Winlator:Android上运行Windows应用的输入控制核心技术
  • Spring Data 2025.0.13 版本发布,或为 3.5.x 系列最后开源版,官方建议升级!
  • 银行AI模型可解释性与连续监控实战指南
  • 2026参观游学考察(标杆企业商务游学考察详细版)
  • 2026年小区家用充电桩推荐,物业易审批、安装友好的合规款
  • AI沙箱代理实战:用Modal实现安全可控的代码操作
  • 3D医学影像AI实战指南:模型选型、数据适配与临床落地
  • Netflix推荐系统背后的用户体验工程实践
  • C++模板与运算符重载实战技巧
  • 如何快速打造你的专属虚拟桌面伴侣:Mate Engine免费开源指南
  • 计算机毕业设计之基于ssm的宠物医院管理系统
  • TVA在物流分拣领域的独特价值(5)
  • 终极指南:如何在Windows系统上完全掌控LG Ultrafine显示器亮度
  • LeetCode 每日一题笔记 日期:2026.06.25 题目:3737. 统计主要元素子数组数目 I
  • 如何用Outfit字体快速打造专业品牌视觉?9种字重免费开源指南
  • Vue 3 setup语法糖用错,数据不更新!
  • 【数据分享】1950-2026年中国0.1°分辨率逐月累积地表径流栅格数据
  • 深入Star Citizen p4k文件解压:技术原理与实战应用
  • 经典算法专区:找树左下角的值(一)