一文吃透 Effect 结构化并发:forkChild、forkDetach、forkScoped 三大纤维 API 完整指南
2026/9/21 16:26:54 网站建设 项目流程

一文吃透 Effect 结构化并发:forkChild、forkDetach、forkScoped 三大纤维 API 完整指南

【免费下载链接】effect-smolCore libraries and experimental work for Effect v4项目地址: https://gitcode.com/GitHub_Trending/ef/effect-smol

effect-smol是 Effect v4 的核心库与实验性代码仓库,其中"结构化并发(structured concurrency)"是它最核心的能力之一:通过forkChildforkDetachforkScoped三个 fork 系列 API,你可以把耗时任务派发到独立纤维(Fiber)上并行执行,同时确保后台任务不会"失控泄漏"。本文将用新手友好的方式,带你快速掌握这三个 API 的区别、选择方法和常用配置项。

什么是 Effect 结构化并发

传统并发容易出现"孤儿线程":主流程结束了,后台任务还在偷偷运行,出了问题无处追踪。Effect 的结构化并发反其道而行——每个子任务都必须明确声明自己"活在谁的生命周期里"

  • 父任务结束 → 子任务自动被中断(防泄漏)
  • 作用域(Scope)关闭 → 作用域内的纤维一起收尾
  • 需要长期运行 → 显式声明"分离",挂到全局作用域

核心思想就一句话:并发可以随意开,但结束时总有人负责清理。

官方在 Effect.ts 文件头注释里就把 "structured concurrency" 列为库的四大支柱之一。

四个 Fork API 一张表看懂

API生命周期归属典型场景源码位置
forkChild父纤维(子作用域)主流程内的并行子任务Effect.ts#L8546
forkScoped当前Scope随作用域一起开/关的任务Effect.ts#L8642
forkIn你指定的任意Scope把任务挂到别的作用域Effect.ts#L8591
forkDetach全局作用域日志、心跳等守护进程Effect.ts#L8692

💡 记忆口诀:Child 跟父走,Scoped 跟作用域走,Detach 谁也不跟。

forkChild 详解:带自动监督的子纤维

forkChild是最高频的 API。它把 Effect 派发到新纤维执行,父纤维结束时,子纤维自动被中断——官方称之为"auto supervision(自动监督)",从根本上杜绝纤维泄漏。

const program = Effect.gen(function*() { const fiber = yield* longTask.pipe(Effect.forkChild) // 立即返回 Fiber // 主流程继续做别的事…… const result = yield* Fiber.join(fiber) // 需要结果时再等待 return result })

文档中还特别提醒:如果只是想要"两个任务并行,取两者结果"这类常见模式,优先用更高层的组合子(如zipParraceWith),直接裸用forkChild反而是最后选择。完整的官方示例就写在 forkChild 定义上方 的注释里。

⚠️ 新手最容易踩的坑:fork 不等于 joinforkChild只是"点火",如果不join它,主流程不会等它完成。

forkScoped 详解:与当前作用域同生共死

forkScoped把纤维挂到当前Scope上:作用域关闭时,纤维自动被中断。它比forkChild多一个Scope环境依赖,适合"任务的生命周期由外部容器决定"的场景,例如 HTTP 请求处理期间启动的轮询、缓存刷新任务——请求结束,Scope 关闭,任务随之清理。

const program = Effect.scoped( Effect.gen(function*() { const fiber = yield* backgroundTask.pipe(Effect.forkScoped) yield* Effect.sleep("1 second") // 函数返回、Scope 关闭时,fiber 自动被中断 return "scope completed" }) )

官方示例见 forkScoped 注释。

forkDetach 详解:独立于主流程的守护进程

forkDetach把纤维挂到全局作用域——主流程结束后,它依然继续运行。这是唯一"逃逸"出结构化并发的官方出口,适合心跳上报、日志守护、全局定时器等真正需要长期存活的后台任务。

const fiber = yield* daemonTask.pipe(Effect.forkDetach) // 主流程 return 后,daemon 仍在后台运行

🎯选择建议:拿不准时默认用forkChild;只有当任务确实在业务主流程结束后仍必须存活时,才用forkDetach——它是 v3 中forkDaemon的新名字,语义就是"脱离父级生命周期"。

通用 Fork 配置项:startImmediately 与 uninterruptible

v4 中四个 fork API 都支持统一选项对象:

  • startImmediately:设为true时新纤维立即开始执行,否则默认延迟启动;
  • uninterruptibletrue使纤维不可被中断,"inherit"继承父纤维的中断策略。
yield* task.pipe(Effect.forkChild({ startImmediately: true }))

这个改动在 v4 迁移文档中有完整说明,详见 migration/forking.md。

拿到结果:Fiber.join 与 Fiber.await 怎么选

fork 之后,你手里是一个Fiber值,等待结果有两个标准入口:

方法返回值适用场景
Fiber.join(fiber)任务的结果(失败则抛出)主流程需要结果
Fiber.await(fiber)Exit值(成功/失败都拿到)只做观测、不传播错误

Fiber.join定义于 Fiber.ts#L272,Fiber.await则返回Exit类型,适合"我只想看它成没成功,不影响主流程成败"的监控型任务。

另外,Effect.awaitAllChildren可以强制当前 Effect 等所有子纤维完成后再结束,定义见 Effect.ts#L8731。

v3 迁移速查:这些 API 改名了

如果你从 Effect v3 迁移过来,重点看这张改名对照表(完整版在 migration/forking.md):

v3v4
Effect.forkEffect.forkChild
Effect.forkDaemonEffect.forkDetach
Effect.forkScoped不变
Effect.forkAll已移除,改用逐个forkChild

延伸阅读与源码地图

  • API 定义总入口:packages/effect/src/Effect.ts("Supervision & Fibers" 区块)
  • 纤维与等待操作:packages/effect/src/Fiber.ts
  • v3→v4 迁移指南目录:migration/
  • 可运行示例:ai-docs/src/01_effect/
  • 官方测试用例(fork 行为的最佳参考):packages/effect/test/Effect.test.ts

小结

  • 默认选forkChild:子纤维跟随父级,自动监督零泄漏;
  • 作用域边界选forkScoped:和 Scope 同生共死;
  • 真·后台任务才选forkDetach:挂全局作用域,主流程退出后继续跑;
  • fork 只是点火,记得用Fiber.join/Fiber.await收尾,才是完整的结构化并发闭环。

掌握这三板斧,Effect v4 的并发模型对你来说就不再是黑盒了 🚀

【免费下载链接】effect-smolCore libraries and experimental work for Effect v4项目地址: https://gitcode.com/GitHub_Trending/ef/effect-smol

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

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

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

立即咨询