依赖与资源
L16 Scope 与资源安全:finalizer 为什么不会漏
本课目标
- 解释 LIFO 释放顺序
- 知道成功 / 失败 / 中断三种退出都会清理
前置课时尚未完成:L15
可以自行跳转学习,但按顺序推进会更省力;学情分析会把这类缺口归入「前置缺口」。
Scope 是资源的生命周期边界
Scope 持有若干 finalizer。关闭 Scope 时按加入的逆序(LIFO)执行它们——和栈展开一致:先关文件,再关连接。Effect.scoped 会创建 Scope、运行 effect、并在结束时关闭它,无论正常成功、失败还是被中断。
acquireRelease 与 addFinalizer
← 代码可左右滑动 →
import { Effect, Console, Exit } from "effect"
const conn = Effect.acquireRelease(
Effect.tryPromise({ try: () => open(), catch: () => new DbError() }),
(c) => Effect.promise(() => c.close())
) // Effect<Conn, DbError, Scope>
const program = Effect.gen(function* () {
const c = yield* conn
yield* Effect.addFinalizer((exit) =>
Console.log("closing with " + exit._tag)
)
return yield* c.query("select 1")
})
await Effect.runPromise(Effect.scoped(program))
// finalizer 逆序执行:后加的先跑💡 acquire 阶段是不可中断的:避免「拿到一半资源却被取消」造成不一致状态。
🔬 与结构化并发的接缝
Scope 也是 Fiber 生命周期的锚点:Effect.forkScoped 把子 fiber 绑到局部 scope,Effect.forkIn 绑到指定 scope。父 fiber 结束不一定杀掉它,但 scope 关闭一定会——取消信号因此跨异步边界传播,无需层层传 AbortController。
交互演练
关键术语
finalizer
注册在 Scope 上的释放动作
acquireRelease
不可中断获取 + 保证释放的资源构造
常见误区
× 在 try/finally 里手写清理,遇到异步中断就失效。
× 把资源存成模块级变量,脱离 Scope 生命周期。
随堂测验(即时反馈,答题计入掌握度)
Q1finalizer 的执行顺序是?
Q2fiber 被中断时,Scope 里的 finalizer?