Effect v3 到 v4 被移除的 Runtime<R> 与 Runtime.runFork 如何迁移到 runForkWith
2026/9/15 14:40:26 网站建设 项目流程

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>RuntimeFlagsFiberRefs打包在一起的单一值:

// v3 interface Runtime<in R> { readonly context: Context.Context<R> readonly runtimeFlags: RuntimeFlags readonly fiberRefs: FiberRefs }

v4 中这个类型不再存在,用Context<R>直接替代。运行函数全部挂在Effect上(如Effect.runForkEffect.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 写法,三处改动:

  1. yield* Effect.runtime<Logger>()改为yield* Effect.context<Logger>(),拿到的services就是一个Context
  2. Runtime.runFork(runtime)(program)改为Effect.runForkWith(services)(program)
  3. 服务提供方从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:

v3v4 替换
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),仅供参考

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

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

立即咨询