跳到主要内容

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​

ParameterTypeDefault valueDescription
labelstring'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​

ParameterTypeDefault valueDescription
setup() => AcquireResultundefined执行副作用并返回其撤销方式;返回 undefined 表示无需释放。 一次 acquire() 只包一步可能抛错的获取——setup 里连做两步而第二步抛错时, 第一步造出的资源不会进入清单,宿主的回滚够不着它。N 个资源就写 N 次 acquire(), 按获取顺序分次登记即可安全回滚。
labelstring'anonymous'诊断标签,缺省 'anonymous'

Returns​

ScopeDisposer

提前单独撤销这一条的句柄。调用后该条目出局,作用域释放时不会再碰它; 重复调用是空操作。句柄不在释放帧里(见 LifecycleScope.dispose 的重入约定): 从它触发的 disposer 里调本作用域的 dispose() 会如实释放整个作用域,而不是被当成 重入而空转。这条不构成互锁(本条目在 disposer 执行之前就已出清单,那一轮释放 不会等它),但它与释放帧内的行为不对称,依赖其中一种写法前请先确认自己在哪条路上。

Throws​

LifecycleScopeDisposedError 作用域已不是 active 时同步抛出, 且 setup 不被执行(不产生新资源)。


child()​

child(label?): LifecycleScope;

Defined in: packages/utils/src/lifecycle/lifecycle-scope.ts:123

在当前作用域内开一个更短命的子作用域。

子作用域在父的登记序列中占创建位置:父释放时它整体在那个位置被释放, 不是全部提前也不是全部推后。子作用域独立释放后会从父清单摘除, 无论其内部撤销是否抛错。

Parameters​

ParameterTypeDefault valueDescription
labelstring'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​

ScopeEntry[]

按登记顺序排列的节点;普通条目的 children 为 [], 子作用域条目递归到底。释放中与已释放的作用域返回空数组。

Remarks​

它只读 this 已经持有的东西,不存在任何跨实例登记——本原语没有全局注册表。 调用不改变任何状态,可重复调用,在 disposing / disposed 上也不抛错: 它是诊断出口,不该成为新的失败源。

用途是把「结构缺陷」(登记时挂错父)与「顺序缺陷」(释放时排序错)分开断言—— 只靠释放后的调用顺序反推,两类失败会混在同一条断言里。