91 lines
3.5 KiB
JavaScript
91 lines
3.5 KiB
JavaScript
import { DisposedError } from "./DisposedError.js";
|
|
/**
|
|
* `AsyncDisposeAggregator` is a utility class that allows you to add multiple items and then dispose them all at once.
|
|
* The items are disposed one by one in the order they were added.
|
|
* You can add a function to call, an object with a `dispose` method, an object with a `Symbol.dispose` method,
|
|
* an object with a `Symbol.asyncDispose` method, or a Promise that resolves to one of the previous types.
|
|
* To dispose all the items, call `dispose` or use the `Symbol.asyncDispose` symbol.
|
|
* The difference between `AsyncDisposeAggregator` and `DisposeAggregator` is that `AsyncDisposeAggregator` can dispose async targets.
|
|
*
|
|
* For example,
|
|
* ```typescript
|
|
* import {AsyncDisposeAggregator, EventRelay} from "lifecycle-utils";
|
|
*
|
|
* const disposeAggregator = new AsyncDisposeAggregator();
|
|
*
|
|
* const eventRelay = new EventRelay<string>();
|
|
* disposeAggregator.add(eventRelay);
|
|
*
|
|
* disposeAggregator.add(async () => {
|
|
* await new Promise(resolve => setTimeout(resolve, 0));
|
|
* // do some async work
|
|
* });
|
|
*
|
|
* disposeAggregator.dispose();
|
|
* ```
|
|
*/
|
|
export class AsyncDisposeAggregator {
|
|
/** @internal */ _targets = [];
|
|
/** @internal */ _disposed = false;
|
|
constructor() {
|
|
this.add = this.add.bind(this);
|
|
this.dispose = this.dispose.bind(this);
|
|
this[Symbol.asyncDispose] = this[Symbol.asyncDispose].bind(this);
|
|
}
|
|
/**
|
|
* Adds a target to be disposed.
|
|
* You can wrap the target with a `WeakRef` to prevent this class from holding a strong reference to the target.
|
|
*/
|
|
add(target) {
|
|
this._ensureNotDisposed();
|
|
this._targets.push(target);
|
|
return this;
|
|
}
|
|
/**
|
|
* Disposes all the targets that have been added and clears the list of targets.
|
|
*/
|
|
async dispose() {
|
|
if (this._disposed)
|
|
return;
|
|
this._disposed = true;
|
|
while (this._targets.length > 0) {
|
|
let disposeTarget = this._targets.shift();
|
|
if (disposeTarget instanceof Promise) {
|
|
try {
|
|
disposeTarget = await disposeTarget;
|
|
}
|
|
catch (err) {
|
|
/* c8 ignore start */
|
|
console.error(err);
|
|
continue;
|
|
} /* c8 ignore stop */
|
|
}
|
|
if (typeof WeakRef !== "undefined" && disposeTarget instanceof WeakRef)
|
|
disposeTarget = disposeTarget.deref();
|
|
if (disposeTarget == null)
|
|
continue;
|
|
else if (Symbol.asyncDispose != null && Symbol.asyncDispose in disposeTarget &&
|
|
disposeTarget[Symbol.asyncDispose] instanceof Function)
|
|
await disposeTarget[Symbol.asyncDispose]();
|
|
else if (Symbol.dispose != null && Symbol.dispose in disposeTarget &&
|
|
disposeTarget[Symbol.dispose] instanceof Function)
|
|
disposeTarget[Symbol.dispose]();
|
|
else if ("dispose" in disposeTarget && disposeTarget.dispose instanceof Function)
|
|
await disposeTarget.dispose();
|
|
else if (disposeTarget instanceof Function)
|
|
await disposeTarget();
|
|
}
|
|
}
|
|
async [Symbol.asyncDispose]() {
|
|
return this.dispose();
|
|
}
|
|
get targetCount() {
|
|
return this._targets.length;
|
|
}
|
|
/** @internal */
|
|
_ensureNotDisposed() {
|
|
if (this._disposed)
|
|
throw new DisposedError();
|
|
}
|
|
}
|
|
//# sourceMappingURL=AsyncDisposeAggregator.js.map
|