Effect.race 和 Effect.raceFirst 有什么区别?

对主库和副本同时发请求,取最快的那个,听起来 Effect.race 就够了。但如果先返回的恰好是个失败,它会怎么样?

Effect.race 取第一个成功的:先结束的如果是失败,它不会马上失败,而是继续等另一个,只有两个都失败才失败。Effect.raceFirst 取第一个结束的,不管成功还是失败。

两者共同点是同时启动所有参赛者,胜出后输家被中断。这个中断依赖结构化并发:子任务属于父任务,父任务不再需要它时就中断它,见 Fiber 与结构化并发。下面看各自适合什么场景。

Effect.race 是怎么选赢家的?

它接收两个 Effect,同时启动,先成功的胜出,另一个被中断。典型用法是对冗余副本发请求,取最快的那个。

import { Effect } from "effect"

declare const fromPrimary: Effect.Effect<string, Error>
declare const fromReplica: Effect.Effect<string, Error>

// 先成功的赢,输的一方被中断(连接会被释放)
const fastest = Effect.race(fromPrimary, fromReplica)

我只想要最快的答案,哪怕是失败,用什么?

用 Effect.raceFirst:先结束的胜出,不管成功还是失败。别把 race 当成「先结束」,它是先成功。

要同时比赛的不止两个怎么办?

Effect.raceAll 对一组 Effect 做 race,语义同 race,推广到多个。

输家被中断,它持有的连接会怎样?

这是个值得停下来想的问题:如果输家持有一个连接,这个连接会怎么样?答案是资源有保证地被释放,机制见 中断与资源安全。

这篇的行为描述来自对 Effect 并发组合子语义的梳理,原文没有标注 effect 版本号,也没有记录本地运行结果,所以这里的结论和代码都没有在本地实际运行验证。API 名和行为以你所用版本的官方文档为准。