`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.
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);}}}