用途
Do notation 是 fp-ts 中用于解决回调地狱的写法。它允许以顺序化、类似命令式的代码风格编写函数式逻辑,同时保持函数纯度与类型安全。
主要能力
核心函数
- Do:创建包裹在 monad 中的空上下文对象
{}。例如TE.Do为TaskEither<never, {}>,E.Do为Either<never, {}>,O.Do为Option<{}>。 - bindTo:当已有值并希望以此启动 Do notation 时使用。语义上等价于
TE.map(user => ({ user })),但更可读。 - bind:用于下一步操作依赖前序值的场景。操作顺序执行,每一步可访问所有前序值,遇到首个错误即短路(对 Either/TaskEither)。回调接收累积的上下文对象。
- apS:用于相互独立的操作,可并行执行(配合 TaskEither)。值立即计算(非惰性),无法访问前序上下文值。
- let:用于从已有值派生的同步计算。纯同步计算,可访问所有前序值,不会失败,适用于转换、计算、格式化。
bind 与 apS 的选择
| 情况 | 使用 | 原因 |
|---|---|---|
| 下一步需要前序结果 | bind | 顺序依赖 |
| 操作相互独立 | apS | 可并行化 |
| 需要转换/计算 | let | 同步且总是成功 |
| 以已有值启动 | bindTo | 比 Do + bind 更简洁 |
性能:顺序与并行
使用 bind 顺序执行时,各步骤依次等待;使用 apS 并行执行时,独立操作可同时运行,总耗时显著降低。
使用方式
资料提供了多个真实示例:
- 用户注册流程:校验输入、检查邮箱可用性、哈希密码、创建用户、发送欢迎邮件,最后返回用户。
- 电商结账:并行获取购物车、用户、地址,校验购物车、检查库存、计算小计/税费/运费/总计、处理支付、创建订单、预留库存、清空购物车、计算预计送达时间。
- ReaderTaskEither 依赖注入:通过
RTE.Do与RTE.ask<Deps>()访问依赖,处理退款流程。 - 累积错误的校验:使用
sequenceS配合校验 applicative 收集所有错误,而非短路。 - 复杂数据聚合:加载仪表盘数据,先获取用户,再并行获取统计、近期订单、通知等。
适用场景
- 需要将多个依赖操作串联且保持可读性的函数式代码。
- 需要并行执行独立异步操作以提升性能。
- 需要依赖注入的工作流(ReaderTaskEither)。
- 需要累积多个校验错误的场景(配合 sequenceS)。
限制
- apS 无法访问前序上下文值,仅适用于独立操作。
- let 仅用于同步纯计算,不能失败。
- 错误累积需使用
sequenceS等 Applicative 组合,而非 Do notation 本身。 - 资料未提供安装命令、收费、价格、平台或效果等信息。