目录 (Outline)
- 一、 错误处理的「暗礁」:为什么 try/catch 不够用了?
- 二、 Effect-TS:全栈 TypeScript 的「瑞士军刀」
- 三、 核心概念:Effect<Success, Error, Requirements>
- 四、 快速上手:构建一个类型安全的 API 调用逻辑
- 五、 实战 1:利用 Layers 实现声明式依赖注入 (DI)
- 六、 实战 2:并发控制与超时处理——一行代码搞定
- 七、 总结:Effect-TS 带来的架构思维革命
一、 错误处理的「暗礁」:为什么 try/catch 不够用了?
1. 历史局限
在传统的 TS 开发中,我们习惯使用 try/catch:
async function getData() {
try {
const res = await fetch('/api');
return await res.json();
} catch (e) {
// 这里的 e 是 any,丢失了错误类型!
console.error(e);
}
}
2. 痛点
- 错误类型丢失:
catch块中的错误无法被编译器追踪。 - 依赖耦合:很难在不修改核心逻辑的情况下替换数据库或 API 客户端。
- 并发难控:管理多个异步任务的超时、重试和竞态条件非常繁琐。
二、 Effect-TS:全栈 TypeScript 的「瑞士军刀」
Effect 是一个专为 TypeScript 设计的函数式编程库。
核心特性
- 显式错误类型:错误也是类型系统的一部分。
- 环境隔离:通过
Requirements实现完美的依赖注入。 - 强大的并发模型:内置
Fiber调度器,支持非阻塞并发。 - 可组合性:所有操作都是纯函数,易于测试和复用。
三、 核心概念:Effect<Success, Error, Requirements>
一个 Effect 可以看作是一个「待执行的任务」,它有三个泛型参数:
- Success:任务成功后返回的数据类型。
- Error:任务可能抛出的错误类型(不再是
any!)。 - Requirements:任务执行所需的依赖环境。
四、 快速上手:构建一个类型安全的 API 调用逻辑
代码示例
import { Effect, Console } from "effect"
// 定义一个可能失败的任务
const getUser = (id: string) =>
Effect.tryPromise({
try: () => fetch(`/api/user/${id}`).then(res => res.json()),
catch: () => new Error("Network Error") // 明确错误类型
})
// 组合逻辑
const program = getUser("1").pipe(
Effect.map(user => `Hello, ${user.name}`),
Effect.flatMap(msg => Console.log(msg))
)
// 执行
Effect.runPromise(program)
五、 实战 1:利用 Layers 实现声明式依赖注入 (DI)
在 Effect 中,你不需要手动传递 db 或 apiClient。
实现步骤
- 定义 Tag:
class Database extends Context.Tag("Database")<Database, { query: ... }>() {}。 - 编写逻辑:在 Effect 中使用
yield* Database获取依赖。 - 注入 Layer:在主程序运行前,通过
provide注入真实的实现。
这种方式让你可以轻松地在测试环境中注入 MockDatabase,而在生产环境注入 PostgresDatabase。
六、 实战 2:并发控制与超时处理——一行代码搞定
场景
并发获取 5 个用户的数据,如果某个请求超过 2 秒则超时,且整体失败时自动重试 3 次。
Effect 实现
const program = Effect.all(
userIds.map(getUser),
{ concurrency: 5 } // 并发数为 5
).pipe(
Effect.timeout("2 seconds"),
Effect.retry({ times: 3 })
)
对比:用传统的 Promise.all 配合 setTimeout 实现这套逻辑至少需要 30 行以上代码,且极易出错。
七、 总结:Effect-TS 带来的架构思维革命
Effect 不仅仅是一个库,它更像是一种全栈架构方案。它借鉴了 Scala (ZIO) 的思想,将函数式编程的严谨性带入了 TypeScript 生态。虽然学习曲线较陡,但对于构建高复杂度、高可靠性的系统来说,Effect 绝对是不二之选。