LifecycleScope
Defined in: packages/utils/src/lifecycle/lifecycle-scope.ts:45
一段代码的存活期:把「取得某种所有权」与「如何放弃它」成对登记, 到期时逆序、串行地全部撤销。
它替代的是散在各处的手写配对——布尔守卫、Map 记账、Array<Subscription> 清单——
那些写法的共同缺陷是「装」和「拆」写在两个地方,新增一处就可能漏配对。
Remarks
语义要点:
- 释放逆序执行(后登记的先撤销),且串行等待异步撤销结算。
- 一个撤销动作抛错不短路其余动作;全部跑完后,恰好一个错误原样重抛,
多于一个以
AggregateError抛出并按执行顺序保留。 - 释放幂等,且成功与失败一视同仁:重复调用返回同一个
Promise实例, 首次失败时返回的就是那个同一个失败结果,不重试、不新增错误。 - 三态
active→disposing→disposed单向推进,不可复活。
本类零外部依赖,不引入进程级注册表、WeakRef 或 FinalizationRegistry;
它也不感知任何响应式订阅类型——订阅由调用方在 setup 里自行返回其退订动作。
Example
const scope = new LifecycleScope('connection-epoch');
scope.acquire(() => {
const timer = setInterval(poll, 1000);
return () => clearInterval(timer);
}, 'poll-timer');
const pluginScope = scope.child('plugin:search');
pluginScope.acquire(() => {
const sub = source$.subscribe(handler);
return () => sub.unsubscribe();
}, 'source-sub');
await scope.dispose(); // pluginScope 随之释放,无需手工记账
Constructors
Constructor
new LifecycleScope(label?): LifecycleScope;
Defined in: packages/utils/src/lifecycle/lifecycle-scope.ts:81
Parameters
| Parameter | Type | Default value | Description |
|---|---|---|---|
label | string | 'anonymous' | 诊断标签,缺省 'anonymous' |
Returns
LifecycleScope
Properties
label
readonly label: string;
Defined in: packages/utils/src/lifecycle/lifecycle-scope.ts:73
只用于诊断与错误消息,不参与身份判定,允许重复。
Accessors
state
Get Signature
get state(): "active" | "disposing" | "disposed";
Defined in: packages/utils/src/lifecycle/lifecycle-scope.ts:76
当前状态。单向推进 active → disposing → disposed,不可复活。
Returns
"active" | "disposing" | "disposed"
Methods
acquire()
acquire(setup, label?): ScopeDisposer;
Defined in: packages/utils/src/lifecycle/lifecycle-scope.ts:101
登记一次「取得所有权 + 如何放弃」。
Parameters
| Parameter | Type | Default value | Description |
|---|---|---|---|
setup | () => AcquireResult | undefined | 执行副作用并返回其撤销方式;返回 undefined 表示无需释放。 一次 acquire() 只包一步可能抛错的获取——setup 里连做两步而第二步抛错时, 第一步造出的资源不会进入清单,宿主的回滚够不着它。N 个资源就写 N 次 acquire(), 按获取顺序分次登记即可安全回滚。 |
label | string | 'anonymous' | 诊断标签,缺省 'anonymous' |
Returns
提前单独撤销这一条的句柄。调用后该条目出局,作用域释放时不会再碰它;
重复调用是空操作。句柄不在释放帧里(见 LifecycleScope.dispose 的重入约定):
从它触发的 disposer 里调本作用域的 dispose() 会如实释放整个作用域,而不是被当成
重入而空转。这条不构成互锁(本条目在 disposer 执行之前就已出清单,那一轮释放
不会等它),但它与释放帧内的行为不对称,依赖其中一种写法前请先确认自己在哪条路上。
Throws
LifecycleScopeDisposedError 作用域已不是 active 时同步抛出,
且 setup 不被执行(不产生新资源)。
child()
child(label?): LifecycleScope;
Defined in: packages/utils/src/lifecycle/lifecycle-scope.ts:123
在当前作用域内开一个更短命的子作用域。
子作用域在父的登记序列中占创建位置:父释放时它整体在那个位置被释放, 不是全部提前也不是全部推后。子作用域独立释放后会从父清单摘除, 无论其内部撤销是否抛错。
Parameters
| Parameter | Type | Default value | Description |
|---|---|---|---|
label | string | 'anonymous' | 诊断标签,缺省 'anonymous' |
Returns
LifecycleScope
新的子作用域
Throws
LifecycleScopeDisposedError 作用域已不是 active 时同步抛出,
不返回任何作用域实例——建子作用域同样是一次登记,适用与
LifecycleScope.acquire 相同的拒绝规则。静默返回一个挂在已释放父作用域下的
子作用域会让调用方以为登记成功了,而它永远不会被释放。
dispose()
dispose(): Promise<void>;
Defined in: packages/utils/src/lifecycle/lifecycle-scope.ts:172
释放本作用域:逆序、串行执行全部撤销动作,然后进入 disposed。
Returns
Promise<void>
全部撤销动作结算后才结算的 Promise。
Throws
恰好一个撤销动作失败时原样重抛该错误;多于一个时抛 AggregateError,
其 errors 按执行顺序排列。无论是否失败,作用域都会进入 disposed。
Remarks
幂等且成功与失败一视同仁:重复调用(含并发重复调用)返回同一个 Promise
实例,首次失败时返回的就是那个同一个失败结果。这里的「不抛新错」只指重复调用本身
不引入新错误,绝不是「把失败的首次释放替换成成功的后续释放」——那会在停机路径的
防御性重复释放里静默吞掉首个真实故障。
disposer 同步地调用本作用域的 dispose()(return () => scope.dispose() 这类写法)
是空操作:清单已经在跑,不重复执行也不自锁。但 disposer 在自己的 await 之后
再调本作用域的 dispose() 属于不支持的写法——那时已经出了同步帧,拿到的是本轮
in-flight 的那个 Promise,而这个 Promise 正等着这条 disposer 返回,必然互锁。
要在异步收尾里释放,释放的应该是子作用域,不是自己。
getEntries()
getEntries(): ScopeEntry[];
Defined in: packages/utils/src/lifecycle/lifecycle-scope.ts:145
读出本作用域清单的树形快照,供在释放之前断言结构。
Returns
按登记顺序排列的节点;普通条目的 children 为 [],
子作用域条目递归到底。释放中与已释放的作用域返回空数组。
Remarks
它只读 this 已经持有的东西,不存在任何跨实例登记——本原语没有全局注册表。
调用不改变任何状态,可重复调用,在 disposing / disposed 上也不抛错:
它是诊断出口,不该成为新的失败源。
用途是把「结构缺陷」(登记时挂错父)与「顺序缺陷」(释放时排序错)分开断言—— 只靠释放后的调用顺序反推,两类失败会混在同一条断言里。