Effect v3 到 v4 被移除的 Runtime 与 Runtime.runFork 如何迁移到 runForkWith
【免费下载链接】effectBuild production-ready applications in TypeScript项目地址: https://gitcode.com/GitHub_Trending/ef/effect
升级 Effect 到 v4 后,一类常见的 v3 代码会直接编译失败:在 effect 内部通过Effect.runtime<R>()取出运行时,再调用Runtime.runFork(runtime)(program)在后台启动另一个带服务依赖的 effect。这段写法在 v4 中无法成立,因为Runtime<R>类型和Runtime.runFork都已被移除。官方迁移文档 migration/runtime.md 给出的替换路径是:用Context<R>承载服务,把Runtime.runFork(runtime)换成Effect.runForkWith(services)。
v4 中 Runtime 模块还剩什么
v3 中Runtime<R>是一个把Context<R>、RuntimeFlags和FiberRefs打包在一起的单一值:
// v3 interface Runtime<in R> { readonly context: Context.Context<R> readonly runtimeFlags: RuntimeFlags readonly fiberRefs: FiberRefs }v4 中这个类型不再存在,用Context<R>直接替代。运行函数全部挂在Effect上(如Effect.runFork、Effect.runForkWith),Runtime模块只剩进程生命周期相关的三个成员:
Teardown— 处理进程退出的接口defaultTeardown— 默认 teardown 实现makeRunMain— 创建平台特定的 main runner
这意味着 v3 里通过 runtime 携带的运行标志、FiberRefs 等概念都不能原样照搬,需要按 v4 的Context和独立 API 重写。
迁移映射:哪些调用换成什么
完整映射见 migration/v3-to-v4.md 与 migration/annotations/effect__Runtime.yaml,与本次迁移直接相关的条目:
| v3 写法 | v4 替换 | 说明 |
|---|---|---|
Effect.runtime<R>() | Effect.context<R>() | 捕获服务为Context,配合对应的run*With函数使用 |
Runtime.runFork(runtime) | Effect.runForkWith(services) | 用原 Runtime 的 Context 运行;无服务依赖时用Effect.runFork |
Runtime.make(...) | Context.make | 构造传给Effect.run*With的服务 Context |
Runtime#Runtime<R>类型 | Context.Context | 直接携带 Context,调用对应的Effect.run*With |
Effect.runForkWith的签名定义在 Effect.ts,@since 4.0.0:
export const runForkWith: <R>( context: Context.Context<R> ) => <A, E>(effect: Effect<A, E, R>, options?: RunOptions | undefined) => Fiber<A, E>注意它是柯里化形式:先传Context,返回一个接收Effect<A, E, R>的函数。同时,effect 的 R 需求必须被这个 Context 满足,否则类型检查不过。
完整迁移示例:带服务的后台 effect
下面是 migration/runtime.md 中的对照示例。v3 版本中,main内部先取 runtime,再用Runtime.runFork(runtime)(program)启动子 effect,服务通过Effect.provideService提供:
v3 代码
import { Context, Effect, Runtime } from "effect" class Logger extends Context.Tag("Logger")<Logger, { readonly log: (message: string) => void }>() {} const program = Effect.gen(function*() { const logger = yield* Logger logger.log("Hello from Logger") }) const main = Effect.gen(function*() { const runtime = yield* Effect.runtime<Logger>() return Runtime.runFork(runtime)(program) }).pipe( Effect.provideService(Logger, { log: (message) => console.log(message) }) ) const fiber = Effect.runFork(main)对应的 v4 写法,三处改动:
yield* Effect.runtime<Logger>()改为yield* Effect.context<Logger>(),拿到的services就是一个Context;Runtime.runFork(runtime)(program)改为Effect.runForkWith(services)(program);- 服务提供方从
Effect.provideService(Logger, value)改为Effect.provideContext(Context.make(Logger, value)),即先构造完整的 Context 再整体提供。
v4 代码
import { Context, Effect } from "effect" class Logger extends Context.Service<Logger, { readonly log: (message: string) => void }>()("Logger") {} const program = Effect.gen(function*() { const logger = yield* Logger logger.log("Hello from Logger") }) const main = Effect.gen(function*() { const services = yield* Effect.context<Logger>() return Effect.runForkWith(services)(program) }).pipe( Effect.provideContext(Context.make(Logger, { log: (message) => console.log(message) })) ) const fiber = Effect.runFork(main)迁移时容易遗漏的一点:v3 的服务定义Context.Tag在 v4 示例中写成了Context.Service加显式标签字符串,两处都要对齐 v4 的 API,否则类型不匹配。
effect 没有服务依赖时
如果后台要启动的 effect 没有 R 需求,不需要runForkWith,直接用Effect.runFork(effect)即可,这是runForkWith(Context.empty())的简写。文档中两条迁移备注都强调了这一点:“Run with the former Runtime's Context; use Effect.runFork when no services are required.”
验证迁移结果
Effect.runForkWith返回Fiber<A, E>,用Fiber.join回到 Effect 域、再用Effect.runPromise取结果即可验证子 effect 真的执行了。Effect 源码中runForkWith的文档示例(Effect.ts)展示了完整的验证写法:
import { Context, Effect, Fiber } from "effect" const output: Array<unknown> = [] interface Logger { log: (message: string) => void } const Logger = Context.Service<Logger>("Logger") const services = Context.make(Logger, { log: (message) => void output.push(message) }) const program = Effect.gen(function*() { const logger = yield* Logger logger.log("Hello from service!") return "done" }) const fiber = Effect.runForkWith(services)(program) void output.push(await Effect.runPromise(Fiber.join(fiber))) output // 文档示例 => ["Hello from service!", "done"]最后一行的["Hello from service!", "done"]是文档示例输出:Fiber.join完成后output中先有服务写下的"Hello from service!",再有 join 的返回值"done",说明子 effect 在提供了服务的 Context 下正确执行并返回了结果。
同一模式的其它 Runner
如果代码里除了runFork还用到了Runtime模块的其它运行函数,替换规则一致——都改成Effect上的run*With,并传入原 Runtime 的 Context:
| v3 | v4 替换 |
|---|---|
Runtime.runPromise(runtime) | Effect.runPromiseWith |
Runtime.runSync(runtime) | Effect.runSyncWith |
Runtime.runCallback(runtime) | Effect.runCallbackWith |
Runtime.runPromiseExit(runtime) | Effect.runPromiseExitWith |
Runtime.runSyncExit(runtime) | Effect.runSyncExitWith |
另外,v3 的RuntimeFlags概念整体被移除,不再随 runtime 传递:需要控制调度、可中断性或运行时指标时,要按 v4 提供的独立配置项分别设置,而不是恢复一个聚合的 flags 对象。
参考资料
- 本次迁移的专题文档:migration/runtime.md
- 完整的 v3 到 v4 API 映射(由 API diff 生成):migration/v3-to-v4.md
Runtime模块逐项替换说明:migration/annotations/effect__Runtime.yaml- v4 中
Effect.runForkWith的定义与用法示例:packages/effect/src/Effect.ts
【免费下载链接】effectBuild production-ready applications in TypeScript项目地址: https://gitcode.com/GitHub_Trending/ef/effect
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考