Effect 4 完全指南:TypeScript 生产级应用的副作用、错误处理与并发原语
【免费下载链接】t3code项目地址: https://gitcode.com/GitHub_Trending/t3/t3code
导读
effect是 Effect 框架的核心包,为 TypeScript 提供了一套构建健壮、可维护、类型安全的生产级应用的基础设施。它围绕「副作用管理」「结构化错误处理」「并发」「资源管理」与「结构化数据」五大主题,提供了从Effect核心抽象到Context/Layer依赖注入、Fiber并发模型、Stream流式处理、Schedule重试策略、Scope资源生命周期再到Schema类型安全编解码的完整标准库。本文以仓库内 packages/effect/README.md 为主线,结合 packages/effect 下的真实源码,系统讲解其核心模块、安装要求、V4 新特性,并给出可直接运行的实战示例。
安装与运行环境要求
安装命令
effect包作为框架核心,以独立 npm 包形式发布,当前仓库中对应版本为4.0.0-rc.112(见 packages/effect/package.json),安装命令为:
npm install effect@rc由于是 monorepo 结构,仓库中实际存在完整的effect包源码目录 packages/effect/src,其中包含了 index.ts 作为包的聚合导出入口(该文件以@barrel: Auto-generated exports自动生成所有模块的export * as ...导出)。
版本与运行时要求
根据 README 的 Requirements 章节,使用effect需要满足:
| 要求 | 说明 |
|---|---|
| TypeScript 5.9+ | 推荐使用 TypeScript 7,以获得最佳性能及与 Effect TypeScript 工具链的兼容性 |
| Node.js 18+ | 仅在 Node.js 环境运行 Effect 时需要 |
strict严格模式 | 必须在tsconfig.json中开启 |
tsconfig.json最小配置如下:
{ "compilerOptions": { "strict": true } }注意:
strict是 Effect 类型推断(尤其是Effect.gen生成器、Context服务类型追踪、Schema编解码类型)能够精确工作的前提,关闭它会显著削弱类型安全保证。
核心模块全景
README 用一个表格概括了effect包中最核心的八个模块,以下逐一展开并结合源码剖析其定位。
Effect:副作用与错误处理的核心抽象
Effect<A, E, R>是描述工作流的惰性值:它可以成功产出A、以类型化错误E失败,并要求环境服务R。关键设计是创建 effect 并不会立即执行任何工作,它只是构建一个可被组合、注入服务、重试、中断、并发执行或交由运行时执行的值。该接口定义于 src/Effect.ts 第 117 行附近:
export interface Effect<out A, out E = never, out R = never> extends Pipeable, Inspectable { readonly [TypeId]: Variance<A, E, R> [Symbol.iterator](): EffectIterator<Effect<A, E, R>> // ... }从 src/Effect.ts 的模块注释可以看到,Effect 值的职责包括:
- 同步、异步、并发与并行交互建模:同一个抽象统一描述同步与异步工作流;
- 基于 Fiber 的并发模型:内置调度、细粒度中断(interruption)、结构化并发(structured concurrency)与高可扩展性;
- 运行时执行:要真正运行一个
Effect值,需要Runtime(参见 src/Runtime.ts)。
一个典型用法:
import { Effect } from "effect" const program = Effect.sync(() => { // 这里可以安全地执行副作用 return Math.random() }) // 通过运行时真正执行 const value = Effect.runSync(program)Context:轻量级依赖注入
Context将服务实现存储在类型化映射中:一个Context以Context.Service或Context.Reference作为键持有服务实现,其类型本身记录着「包含哪些键」。Effect 使用 Context 作为其环境(environment),因此服务只需注入一次,无需在每个函数调用间显式传递。相关定义见 src/Context.ts:
Key是某个服务的类型化句柄:Identifier在 Effect 类型中追踪需求,Shape是实际取回的服务实现;Key本身也是一个Effect值,因此在Effect.gen中直接yield*一个 key 就能从当前 Fiber 上下文中取出对应服务;- 模块提供创建键、构建上下文、添加/读取服务、合并上下文、选择或移除服务的完整辅助函数。
Layer:可组合的依赖装配系统
Layer<ROut, E, RIn>描述「如何获取一个或多个服务、构建它们需要哪些服务、获取过程可能产生哪些错误」。Layer 可以管理作用域内资源、对共享服务做记忆化(memoize)、与其他 Layer 组合、将服务提供给 Effect 或 Stream,并挂接错误处理、追踪或生命周期钩子。核心模型见 src/Layer.ts 模块注释。它让依赖装配具备模块化与可组合性,是大型应用组织服务边界的核心手段。
Fiber:轻量级虚拟线程
Fiber<A, E>是对一个Effect执行过程的句柄——它可能仍在运行,也可能已经完成。Fiber 是 Effect 的并发单元,提供了在保持结构化并发与取消安全(cancellation safety)的前提下并发运行多个 Effect 的能力(见 src/Fiber.ts)。该模块支持:
await/join等待或汇合 Fiber 结果;- 中断单个或多个 Fiber(
interrupt); - 检查未知值;
- 访问当前 Fiber(
currentFiber); - 将手动管理的 Fiber 挂接到
Scope以便统一清理。
配合Effect.fork使用:
import { Effect } from "effect" const program = Effect.gen(function* () { const fiber = yield* Effect.fork(Effect.sync(() => "hello")) const result = yield* Fiber.join(fiber) return result })Stream:异步事件驱动数据处理
Stream是处理异步、事件驱动数据的强大抽象,用于按需(pull-based)消费和变换数据序列,同时保持 Effect 的资源安全与背压语义。对应的实现位于 src/Stream.ts,它与Channel(底层协议)、Pull(拉取原语)、Take(批次数据)等模块协同工作。
Schedule:重试与重复策略
Schedule<Output, Input, Error, Env>描述重试、重复与节奏(pacing)的策略:调度器以输入值驱动逐步推进,每一步要么停止,要么产出一个输出及下一次执行前的延迟。Schedule 被retry、repeat、stream与channel等 API 用来决定何时继续、等待多久、何时停止(见 src/Schedule.ts)。
Schedule在 v4 中引入了InputMetadata元数据(src/Schedule.ts 第 63 行附近),提供给调度函数包含计时与输入信息:input、attempt、start、now、elapsed、elapsedSincePrevious,使自定义调度策略可以基于真实时间信息决策。
Schedule自带可组合的调度策略,例如指数退避:
import { Effect, Schedule } from "effect" const retryPolicy = Schedule.exponential("100 millis").pipe( Schedule.recurs(5) // 最多重试 5 次 ) const program = Effect.sync(() => { /* 可能失败的副作用 */ }).pipe( Effect.retry(retryPolicy) )README 中官方示例还演示了与TestClock配合进行确定性测试(见 src/Schedule.ts 第 37-48 行):
import { Effect, Schedule } from "effect" import { TestClock } from "effect/testing" const executions: Array<number> = [] const program = Effect.sync(() => executions.push(executions.length + 1)).pipe( Effect.repeat(Schedule.recurs(2)), Effect.as(executions) ) await Effect.runPromise(Effect.provide(program, TestClock.layer())) // => [1, 2, 3]Scope:资源生命周期管理
Scope是一个「生命周期边界」(lifetime boundary):代码可以在其上注册清理 Effect,关闭 Scope 时会携带结束工作的Exit值执行所有清理。绝大多数应用代码使用更高级的Effect.scoped与Layer,而Scope模块本身适合需要直接创建、提供、fork、关闭或检查 Scope 的场景(见 src/Scope.ts)。
关键实现细节:
Scope支持sequential与parallel两种终结策略(src/Scope.ts 第 47 行readonly strategy: "sequential" | "parallel");Closeable扩展了基础Scope接口,能够关闭并执行全部注册的 finalizer;- Scope 内部状态机包含
Open | Closed | Empty三种状态,官方示例展示了从Empty状态经注册资源后关闭变更为Closed的完整过程。
import { Effect, Exit, Scope } from "effect" const program = Effect.gen(function* () { const scope = yield* Scope.make("sequential") yield* Scope.addFinalizer(scope, () => Effect.sync(() => console.log("cleanup"))) yield* Scope.close(scope, Exit.void) }) Effect.runSync(program)Schema:类型安全的编码与解码
Schema是一个用于定义、校验、转换结构化数据的强大库,提供类型安全的编码与解码能力,其实现位于 src/Schema.ts(配套的 AST 与解析基础设施见 src/SchemaAST.ts、src/SchemaParser.ts)。通过一个 Schema 声明即可同时获得运行时校验与静态类型:
import { Schema } from "effect" const User = Schema.Struct({ id: Schema.Number, name: Schema.String }) // 编码时校验、类型安全 const user = Schema.decodeSync(User)({ id: 1, name: "Alice" })丰富的标准库
除了上述八个核心模块,effect包还包含数量众多的数据结构与工具模块,它们共同构成「TypeScript 缺失的标准库」。从 packages/effect/src 目录可以看出完整清单,例如:
- 不可变集合:
Array、Chunk、List、HashMap、HashSet、Record、Trie、RedBlackTree、SortedSet、SortedMap等; - 函数式工具:
Option、Either、Result、Exit、Cause、Predicate、Function、Pipeable、Match、Data、Equal、Hash等; - 并发与同步原语:
Queue、PubSub、Ref、SynchronizedRef、Deferred、Semaphore、RateLimiter、Mailbox、Latch等; - 资源池化:
Pool、KeyedPool、Cache、ScopedCache、ScopedRef等; - 配置与观测:
Config、ConfigProvider、Logger、Metric、Tracer、LogLevel、Cron等; - 其他:
Duration、DateTime、Clock、Random、Encoding、Brand、Redacted、JsonSchema、Match、Optic等。
包描述(见 packages/effect/package.json)将自身定位为 "The missing standard library for TypeScript, for writing production-grade software",关键词为typescript、error-handling、concurrency、observability。
V4 新架构:内置unstable/*命名空间
README 明确指出:在 v4 中,原先位于独立包中的功能被合并进effect包,以effect/unstable/*命名空间提供,涵盖http、httpapi、rpc、cluster、workflow、cli、ai、sql与reactivity等领域。从 packages/effect/package.json 的exports字段可以精确验证这些子路径:
| 子路径 | 对应目录 |
|---|---|
effect/unstable/http | packages/effect/src/unstable/http |
effect/unstable/httpapi | packages/effect/src/unstable/httpapi |
effect/unstable/rpc | packages/effect/src/unstable/rpc |
effect/unstable/cluster | packages/effect/src/unstable/cluster |
effect/unstable/workflow | packages/effect/src/unstable/workflow |
effect/unstable/cli | packages/effect/src/unstable/cli |
effect/unstable/ai | packages/effect/src/unstable/ai |
effect/unstable/sql | packages/effect/src/unstable/sql |
effect/unstable/reactivity | packages/effect/src/unstable/reactivity |
此外还有encoding、devtools、eventlog、observability、persistence、process、socket、schema、workers等命名空间。实际目录 packages/effect/src/unstable 中恰好存在这些子目录,与 package.json 的导出声明一一对应。
以unstable命名,意味着这些 API 仍在演进、可能在未来版本中调整。使用方式:
import { HttpRouter, HttpServer } from "effect/unstable/http"另外effect/testing子路径(对应 packages/effect/src/testing)提供TestClock、TestContext等确定性测试工具,便于在不依赖真实时钟/环境的前提下测试调度与并发逻辑。
包的分发结构
从 packages/effect/package.json 还可以确认以下工程事实:
- 模块系统:
"type": "module",面向 ESM 环境; - sideEffects 为空数组:
"sideEffects": [],所有模块可被 tree-shaking 安全移除; - 依赖极少:运行时仅依赖
fast-check(属性测试)与msgpackr(二进制序列化); - 开发依赖:
ajv(JSON Schema 校验)、immer、protobufjs、tinybench、valibot等,用于测试与工具链; - 发布物:
files字段包含src/**/*.ts、dist/**/*.js与类型声明、ai-docs/**/*等,源码随包一并发布,便于调试与内省。
小结
effect包是 Effect 框架的基石,把「类型安全的副作用、结构化错误处理、基于 Fiber 的并发、可组合的资源生命周期与依赖装配、流式处理与重试策略、类型安全的 Schema 编解码」统一进一个抽象体系,并内置了覆盖日常开发所需的数据结构与工具的标准库。V4 更将 HTTP、RPC、SQL、CLI、AI、Cluster、Workflow 等领域能力收编进effect/unstable/*命名空间,让单一依赖即可支撑从后端服务到分布式工作流的完整应用开发。若要深入源码,可以从 packages/effect/src/Effect.ts 与 packages/effect/src/index.ts 开始阅读。
【免费下载链接】t3code项目地址: https://gitcode.com/GitHub_Trending/t3/t3code
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考