TypeScript 里依赖藏在 import 里有什么问题?
把数据库、邮件客户端写成模块级单例,签名读不出依赖,测试和替换都只能整个文件地换。手动传参和 DI 容器各解决一半,Effect 的 R 参数把两半合在一起。
哈喽,我是颜sir,询盘自由网背后的人。做网络营销快6年,运气不错,实现了财务自由,目前全职在家钻研更多赚钱方法。在钻研过程中,我发现网上几乎找不到优质的中文资源。我深知新手入门需经历痛苦挣扎,所以创建了询盘自由网,把我知道的分享给你。
把数据库、邮件客户端写成模块级单例,签名读不出依赖,测试和替换都只能整个文件地换。手动传参和 DI 容器各解决一半,Effect 的 R 参数把两半合在一起。
接口按能力而不是技术划分,方法不带 R,错误只暴露调用方能处理的,key 用 包名/服务名。还有哪些东西不必做成服务。
每个测试各自 provide 一份 Layer 最隔离,但构建贵的资源会重复构建。@effect/vitest 的 layer 可以让一个 describe 块共享一次构建,代价是共享状态。
被测代码有 5 秒超时或重试退避,测试要么真等,要么把时间调短。用 it.effect 自带的 TestClock,fork 出去再推进虚拟时间,测的就是线上用的那个超时。
Effect.repeat 是 retry 的镜像:成功之后再来一次。用 Schedule.spaced 配 until 条件就是一个轮询,同一个 Schedule 值也能喂给 retry。
用 Effect.retry 的 while 条件按错误类型筛选,只重试网络抖动和限流,不重试校验失败和资源不存在;缺陷和中断本来就不会被重试。
timeout 放在 retry 里面,限制的是每次尝试;放在外面,限制的是总预算。这篇讲清两种写法的差别,以及超时后底层请求到底有没有被取消。
v4 里 STM 模块没有了,换成 Effect.tx 加一组 Tx 数据结构。这篇讲事务模型怎么运转,以及它能保证 Ref 保证不了的什么。
Effect.tx 的事务体会重跑,里面的副作用会重复;内层事务失败也不会单独回滚。这篇用可运行的代码把这几个坑摆出来。
事务里余额不足、队列为空时,不用轮询也不用手拼 Deferred,一行 Effect.txRetry 就能让事务阻塞到相关状态变化。
单值用 Ref,能合并就合并成一个 Ref,多个结构要原子加等条件才用 Tx,要互斥执行异步副作用用 Semaphore。这篇给出判断顺序。
async 函数的签名只写了成功值,失败方式、依赖、取消都在类型之外。这篇说明 Effect 怎样用 Effect<A, E, R> 把它们写进类型,以及它值不值得用。
Promise 创建时就已经在执行,Effect 创建时什么都不会发生。这篇讲清 Effect 是「描述」而不是「过程」,以及它为什么能重试、能被运行器接管。
用 Effect.gen 把顺序逻辑写得像 async/await,yield* 负责运行与错误传播,E 和 R 自动累积。再讲 gen 与 pipe 的分工,以及 Effect.fn 命名函数。
把 JSON.parse、fetch 这类已有代码接进 Effect,关键是决定失败算预期错误还是缺陷。这篇按 sync、try、promise、tryPromise 的选择路径讲清楚。
Effect 把出错分成进类型的 failure 和不进类型的 defect。这篇给出一个判断标准,并讲清 Effect.fail、Effect.die 和 Effect.try 各自落在哪条通道。
有了类型化错误之后,真正的难题是归属:谁来捕获、谁来决策、谁来呈现。这篇给出一套按「谁有足够上下文」划分的判断方法。
TypeScript 类型编译后就被擦除,从外部进来的数据在运行时只是 unknown。用 Effect 的 Schema 把类型和校验写成一份定义,在边界解码一次,内部就能放心信任类型。
形状正确的数据,含义仍可能是错的。用 transform、brand、判别联合三个工具,把外部表示、值的身份和合法状态写进 Schema,让运行时和类型系统共用同一份规则。
用 Context.Service 定义服务接口,业务代码 yield* 取用,依赖自动记进 Effect 的 R 参数,实现留到入口用 Effect.provide 提供。换实现不用改业务代码。