Effect 的 fiber 什么时候该脱离父级?

一次请求触发了一个后台同步,请求响应已经返回了,同步却被一起中断,只做了一半。

这种任务该用 Effect.forkDetach,让子 fiber 脱离父级;但默认仍然应该用 Effect.forkChild,脱离归属必须是有意识的决定。 forkChild 创建的子 fiber 属于父,父结束时会被自动中断;forkDetach 创建的子 fiber 挂在全局,不随父结束。

之所以默认不脱离,是因为 Effect 的并发是结构化的:子 fiber 的生命周期被包含在父 fiber 之内,这样才不会留下无主的孤儿任务。下面看哪些情形值得打破这条规则,打破之后又由谁来负责。

为什么默认让子任务跟着父走?

Effect.forkChild 在当前 fiber 之下启动子 fiber。父 fiber 结束(成功、失败或被中断)时,还没结束的子 fiber 会被自动中断;父被中断时,中断也会向下传递。这样用户离开页面、父任务失败这类情形,都不会留下没人管的任务,原理见 Effect 的 fiber 是什么,为什么子任务归父任务管?。

forkDetach 脱离之后谁负责?

有些任务本来就不该跟着当前请求一起死,比如一次触发后要跑完的后台同步。这时用 Effect.forkDetach:子 fiber 挂在全局,不随父结束,它的生命周期需要你自己负责。

脱离意味着你放弃了结构保证。没人 join、也没人中断的话,它出了错就没人看见。

forkScoped 和 forkIn 是怎么回事?

Effect.forkScoped 和 Effect.forkIn 提供的是第三种归属:把子 fiber 挂在某个 Scope 上,Scope 关闭时中断。这样任务既不跟着当前父 fiber,也不是无主,而是跟着一个你能明确指出的资源生命周期。Scope 的用法见 Effect 的资源与 Scope。

怎么选?

  • 任务应该随当前流程结束:forkChild,这是默认。
  • 任务要在当前流程之外跑完,生命周期由你自己管:forkDetach。
  • 任务应该跟着某个资源的生命周期:forkScoped 或 forkIn。

这几个 fork 的更多细节,比如 fork 返回时子 fiber 还没真正开始执行带来的竞态,见 Effect fork 家族。

本文整理自 Effect 教程中关于 fiber 的章节,没有再亲自运行过相关代码,也没有核对具体的 effect 版本号。forkScoped 与 forkIn 的具体用法在原文里只是点到,细节请以你所用版本的文档为准。