怎么把回调式 API 包装成 Effect?
fs.readFile(path, cb) 没有返回 Promise,所以 Effect.promise 和 Effect.tryPromise 都无从下手。而且回调里的 err 类型是 Error | null,失败信息本来就不精确。
答案是:用 Effect.callback。 它给你一个 resume,你在回调触发时调用它,传入「最终应该变成什么 Effect」,成功就传 Effect.succeed,失败就传 Effect.fail。
这个写法还带来一个好处:注册函数可以返回一个清理 Effect,在被中断时运行,这就是回调世界里的资源安全。如果你想了解整组构造函数怎么选,见 怎么把已有的同步、Promise 代码包装成 Effect?
Effect.callback 的基本写法是什么?
const readText = (path: string) =>
Effect.callback<string, FileError>((resume) => {
readFile(path, "utf8", (err, data) => {
if (err) resume(Effect.fail(new FileError({ cause: err })))
else resume(Effect.succeed(data))
})
})注册函数收到一个 resume,在回调触发时调用它。有三个要点:
resume只有第一次调用有效,后续调用会被忽略- 注册函数还会收到
AbortSignal作为第二个参数 - 注册函数可以返回一个清理 Effect,当这个 Effect 被中断时运行
被中断时怎么清理定时器?
const sleep1s = Effect.callback<void>((resume) => {
const t = setTimeout(() => resume(Effect.void), 1000)
return Effect.sync(() => clearTimeout(t)) // 被中断时清掉定时器
})返回的清理 Effect 在被中断时运行,定时器被清除,不会在后台泄漏。
v3 里的 Effect.async 去哪了?
v3 里这个构造函数叫 Effect.async,在 4.0.5 里已经不存在,对应的名字是 Effect.callback。从 v3 升级时要改名,其他 v3 到 v4 的变化见 Effect v3 到 v4 迁移。
本文依据 effect 4.0.5 的行为整理,Effect.async在该版本不存在、对应名字为Effect.callback也特指这个版本。示例代码没有在本地逐条运行验证。