lifecycle-utils
Version:
A set of general utilities for the lifecycle of a JS/TS project/library
93 lines • 3.36 kB
JavaScript
import { DisposedError } from "./DisposedError.js";
/**
* `DisposeAggregator` is a utility class that allows you to add multiple items and then dispose them all at once.
* You can add a function to call, an object with a `dispose` method, or an object with a `Symbol.dispose` method.
* To dispose all the items, call `dispose` or use the `Symbol.dispose` symbol.
*
* For example,
* ```typescript
* const disposeAggregator = new DisposeAggregator();
*
* const eventRelay = new EventRelay<string>();
* disposeAggregator.add(eventRelay);
*
* const eventRelay2 = disposeAggregator.add(new EventRelay<string>());
*
* disposeAggregator.dispose();
* console.log(eventRelay.disposed === true); // true
* console.log(eventRelay2.disposed === true); // true
* ```
*/
export class DisposeAggregator {
/** @internal */ _targets = [];
/** @internal */ _disposed = false;
constructor() {
this.add = this.add.bind(this);
this.dispose = this.dispose.bind(this);
this[Symbol.dispose] = this[Symbol.dispose].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 target;
}
/**
* Disposes all the targets that have been added and clears the list of targets.
*
* After all the targets have been disposed, if any throws an error,
* then the error from the first such target will be thrown.
*/
dispose() {
if (this._disposed)
return;
this._disposed = true;
let firstError;
let hasError = false;
const targets = this._targets;
this._targets = [];
for (let i = 0; i < targets.length; i++) {
let disposeTarget = targets[i];
targets[i] = null;
try {
if (typeof WeakRef !== "undefined" && disposeTarget instanceof WeakRef)
disposeTarget = disposeTarget.deref();
if (disposeTarget == null || disposeTarget === this)
continue;
else if (typeof disposeTarget === "function") {
if (disposeTarget === this.dispose || disposeTarget === this[Symbol.dispose])
continue;
disposeTarget();
}
else if (Symbol.dispose != null && Symbol.dispose in disposeTarget && typeof disposeTarget[Symbol.dispose] === "function")
disposeTarget[Symbol.dispose]();
else if ("dispose" in disposeTarget && typeof disposeTarget.dispose === "function")
disposeTarget.dispose();
}
catch (err) {
if (!hasError) {
firstError = err;
hasError = true;
}
}
}
targets.length = 0;
if (hasError)
throw firstError;
}
[Symbol.dispose]() {
this.dispose();
}
get targetCount() {
return this._targets.length;
}
/** @internal */
_ensureNotDisposed() {
if (this._disposed)
throw new DisposedError();
}
}
//# sourceMappingURL=DisposeAggregator.js.map