javascript / expert
Snippet
Deterministic Teardown Ordering in Nested DestroyRef Hierarchies
`DestroyRef.onDestroy` callbacks run in registration order per injector, but resources registered dynamically at runtime — sockets opened by lazy-loaded feature components, for example — need reverse-of-acquisition teardown to avoid closing a shared dependency before the handle that still references it. This pool tracks insertion order in a `Map` and tears down in reverse, and the `closing` flag closes anything registered mid-teardown immediately instead of leaking it into a set that will never be iterated again. Without the flag, a component destroyed by its own `ngOnDestroy` during the pool's teardown loop could register a handle that silently never gets closed.
snippet.js
javascript
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
import { Injectable, DestroyRef, inject } from '@angular/core';@Injectable()export class ResourcePool {private handles = new Map<string, { close(): void }>();private closing = false;private destroyRef = inject(DestroyRef);constructor() {this.destroyRef.onDestroy(() => this.teardown());}register(key: string, handle: { close(): void }): void {if (this.closing) {handle.close();return;}this.handles.get(key)?.close();this.handles.set(key, handle);}private teardown(): void {this.closing = true;for (const [key, handle] of [...this.handles].reverse()) {handle.close();this.handles.delete(key);}}}
angular
Breakdown
1
this.destroyRef.onDestroy(() => this.teardown());
Ties cleanup to the injector's own lifecycle rather than a component's `ngOnDestroy`, so the pool tears down correctly even when provided at a route or lazy-loaded injector level.
2
for (const [key, handle] of [...this.handles].reverse()) {
Snapshots entries into an array before reversing, since mutating a `Map` during iteration over its own live iterator has undefined-order pitfalls.
3
if (this.closing) { handle.close(); return; }
Catches registrations that arrive after teardown started, closing them immediately so they never sit unclosed in a `Map` nobody will drain again.