Effect 深度课effect@4.1.4 · LTS
心智模型与准备

L01 为什么需要 Effect:TypeScript 的四个缺口

⏱ 20 min难度 1/5前置:无
本课目标
  • 说清 Promise / try-catch 在生产代码里的结构性缺陷
  • 理解 Effect 要解决的是「程序」的类型化,而不是数据

数据有类型,程序没有

TypeScript 能精确描述一个 User 对象,但一个函数签名几乎不告诉你:它可能怎么失败、需要哪些外部服务、能不能被安全地重试/超时/取消。于是这些保证只能靠散落的 try/catch、AbortController、手写 retry 循环和口头约定来维持,且彼此无法组合。

四个具体缺口

  • 错误无类型:Promise 的 rejection 是 any,catch (e: unknown) 之后只能靠猜。
  • 依赖靠传参:配置、DB、Logger 层层透传,测试替换实现要改动调用链。
  • 取消不安全:AbortSignal 需要手动串联,资源清理与超时竞态容易泄漏。
  • 并发无结构:Promise.all 一个失败,其余任务仍在后台跑,无法统一回收。
同一段逻辑的两种写法
← 代码可左右滑动 →
// Promise:立即执行、错误无类型、取消靠外部约定
const getTodo = async (id: number) => {
  const res = await fetch("/todos/" + id)
  return res.json() // 可能抛任何东西
}

// Effect:只描述、不执行;E 与 R 都留在类型里
import { Effect } from "effect"

const getTodo = (id: number) =>
  Effect.tryPromise({
    try: () => fetch("/todos/" + id),
    catch: () => new HttpError("request failed")
  }).pipe(Effect.andThen((res) => Effect.promise(() => res.json())))

// 类型:Effect<any, HttpError, never> —— 还没运行过一次
💡 注意 Effect.tryPromise 的 catch 把「任意异常」收敛成一个有名字的错误类型。
🔬 它不是代数效应语言

Effect 没有用户可自定义的 perform / handler,也没有语言级可恢复续体。它只有一个固定的解释器:fiber runtime,解释一组固定的内部指令。这是「工业级 effect 框架」,灵感来自 ZIO / Cats Effect,而不是 Koka / OCaml 5 那类 algebraic effects。

交互演练
关键术语
副作用 (effect)
与外部世界交互的行为,被建模成值而不是立即执行
programs as values
把程序本身当作可传递、可组合、可检查的不可变数据
常见误区
× 把 Effect.succeed(x) 当成 Promise.resolve(x):前者只是描述,后者已开始执行。
× 以为引入 Effect 要重写整个项目——实际可以从最复杂的一小块开始渐进替换。
随堂测验(即时反馈,答题计入掌握度)
Q1Effect 与 Promise 最本质的区别是什么?
Q2下面哪一项不是 Effect 直接解决的痛点?