Effect 在 Workers 上有哪些已知的 issue?

在 Workers 上,Effect 的 HttpClient 有一条路径会让 Worker 记录一条 Disallowed operation called within global scope 的未捕获错误;另一个问题是,一个比请求活得更久的 fiber 可能挂住。

答案是:我读到两个相关的 GitHub issue,issue 页面显示都已被处理,你用的版本可能已经修复;我没有在 Workers 上复现过,不当作确定事实。保守的做法是读取或丢弃每一个响应体,并且不让 fiber 的寿命超过单个请求。

两个问题的共同点是都发生在「请求上下文之外」:Workers 里 I/O 与 timer 绑定在发起它的请求上,global scope 里还有额外限制。Effect 在这个平台上的整体写法见 Effect 在 Workers 上怎么组织 Runtime?。下面分别说我读到了什么,以及哪一部分我确认过。

HttpClient 未读取的响应会出什么问题?

issue #9543 报告:在 Workers 上,HttpClient 对没有被 scope 管理的响应,会登记到模块级的 FinalizationRegistry,被 GC 时回调里执行 controller.abort();这个回调发生在请求上下文之外,于是 Worker 记录一条全局作用域禁止操作的未捕获错误。issue 页面显示它已被 PR #9547 关闭(2026-10-19)。

我能确认的只有一点:我读了 4.0.5 的 dist/http/HttpClient.js,里面确实有一个模块级的 FinalizationRegistry,回调是 controller.abort()。4.0.5 发布于 2026-10-15,早于 #9547,所以 4.0.5 不会包含这个修复;是否在后续版本修复、具体改了什么,我没有验证。

怎么规避它?

规避思路与平台无关:读取或显式丢弃每一个响应体,不要让响应对象靠 GC 收场。HttpClient 本身的用法见 Effect 的 HTTP:把状态码、超时和运行时差异都写进类型。

比请求活得更久的 fiber 会怎样?

issue #9025 报告:在 Workers 上,一个比发起请求活得更久的长寿 fiber,如果它下一步的调度 timer 属于已结束的请求,就可能挂起;之后等待它的请求也会一起挂住。issue 页面显示它已关闭。

我读到的信息是:PR #9032 的方案(改为 microtask 调度)没有合并,另一个文档性质的 PR #9033 合并了,内容是说明 Workers 的限制。具体当前行为我没有验证。

长寿 fiber 的保守做法是什么?

不依赖这些修复:在 Workers 上不要让 fiber 的寿命超过单个请求,需要常驻的后台逻辑交给 Durable Objects 或队列,而不是 isolate 里的长寿 fiber。这个建议是我的推论,没有验证过。

需要后台工作时,用 ctx.waitUntil(runtime.runPromise(...)) 把它挂在请求的生命周期上,而不是 runFork 出去就不管。

本篇没有任何一条是我在 Workers 上跑出来的:两个 issue 的内容、状态和 PR 去向都是我读 GitHub 页面得到的,页面状态可能已变化,请以你实际使用版本对应的 issue 为准。唯一自己确认过的是 effect 4.0.5 的 dist/http/HttpClient.js 里有模块级 FinalizationRegistry 且回调是 controller.abort()(4.0.5 发布于 2026-10-15)。ctx.waitUntil 的用法是 Workers 的通用用法,我没有在 workerd 里验证;Durable Objects 或队列的建议是推论。