demo · v131
Component Lifecycle
A custom element that keeps an AsyncDisposableStack alive for the whole DOM lifetime: connectedCallback() registers an IntersectionObserver, a ResizeObserver, and an interval timer, while disconnectedCallback() calls disposeAsync() so none of those resources keep running after removal.
Checking AsyncDisposableStack support…
live components
No components mounted.
Active components: 0 · Active resources: 0 · Disposed: 0
lifecycle log
—waiting…
the code
The key detail is that the stack survives until the element leaves the DOM. Do not wrap connectedCallback() in await using; that lexical scope exits immediately after setup.
class LiveCard extends HTMLElement {
#resources = null;
async connectedCallback() {
if (this.#resources) return;
const stack = new AsyncDisposableStack();
const io = new IntersectionObserver((entries) => this.#onIntersect(entries));
io.observe(this);
stack.defer(async () => io.disconnect());
const ro = new ResizeObserver(() => this.#onResize());
ro.observe(this);
stack.defer(async () => ro.disconnect());
const id = setInterval(() => this.#onTick(), 1000);
stack.defer(async () => clearInterval(id));
this.#resources = stack;
}
async disconnectedCallback() {
const stack = this.#resources;
this.#resources = null;
await stack?.disposeAsync();
}
}