@akala/core
Version:
193 lines • 5.7 kB
JavaScript
/**
* Checks if an object is a Promise-like instance
* @template T - The expected resolved value type
* @param {T | PromiseLike<T>} o - The object to check
* @returns {boolean} True if the object has a 'then' method, indicating it's Promise-like
*/
export function isPromiseLike(o) {
return o && o['then'] && typeof (o['then']) == 'function';
}
export function combineSubscriptions(...subs) {
return () => {
let unsubscribed = false;
for (const sub of subs) {
const result = sub && sub();
unsubscribed ||= result;
}
return unsubscribed = true;
};
}
export function teardown(subscription, abort) {
abort.addEventListener('abort', () => subscription());
}
export function combineAsyncSubscriptions(...subs) {
return async () => {
let unsubscribed = false;
for (const sub of subs) {
const result = sub && await sub();
unsubscribed ||= result;
}
return unsubscribed;
};
}
/**
* Manages cleanup of subscriptions and disposable resources
*/
export class TeardownManager {
/**
* @param subscriptions - Optional initial array of teardown subscriptions
*/
constructor(subscriptions) {
if (subscriptions)
this.subscriptions = subscriptions;
}
subscriptions = [];
/**
* Cleans up all registered subscriptions (implements Disposable pattern)
*/
[Symbol.dispose]() {
this.subscriptions.forEach(s => s());
this.subscriptions.length = 0;
}
/**
* Registers a teardown subscription or Disposable
* @typeParam T - Subscription function or Disposable object
* @param sub - Subscription callback or Disposable object to register
* @returns The original subscription for chaining
*/
teardown(sub) {
if (!sub)
return sub;
if (Symbol.dispose in sub) {
this.subscriptions.push(() => {
sub[Symbol.dispose]();
return true;
});
}
else
this.subscriptions.push(sub);
return sub;
}
}
/**
* Manages cleanup of subscriptions and disposable resources
*/
export class AsyncTeardownManager {
/**
* @param subscriptions - Optional initial array of teardown subscriptions
*/
constructor(subscriptions) {
if (subscriptions)
this.subscriptions = subscriptions;
}
subscriptions = [];
/**
* Cleans up all registered subscriptions (implements Disposable pattern)
*/
async [Symbol.asyncDispose]() {
await Promise.all(this.subscriptions.map(s => s()));
this.subscriptions.length = 0;
}
/**
* Cleans up all registered subscriptions (implements Disposable pattern)
*/
[Symbol.dispose]() {
const subs = this.subscriptions.slice(0);
this.subscriptions.length = 0;
subs.map(s => s());
}
/**
* Registers a teardown subscription or Disposable
* @typeParam T - Subscription function or Disposable object
* @param sub - Subscription callback or Disposable object to register
* @returns The original subscription for chaining
*/
teardown(sub) {
if (!sub)
return sub;
if (isPromiseLike(sub))
return sub.then(s => { this.teardown(s); return s; });
if (Symbol.dispose in sub)
this.subscriptions.push(() => {
sub[Symbol.dispose]();
return Promise.resolve(true);
});
else if (Symbol.asyncDispose in sub)
this.subscriptions.push(async () => {
await sub[Symbol.asyncDispose]();
return true;
});
else
this.subscriptions.push(sub);
return sub;
}
}
export class StatefulSubscription {
_unsubscribe;
_unsubscribed = false;
constructor(_unsubscribe) {
this._unsubscribe = _unsubscribe;
}
unsubscribe = () => {
if (this._unsubscribed)
return false;
this._unsubscribe();
return this._unsubscribed = true;
};
get unsubscribed() {
return this._unsubscribed;
}
[Symbol.dispose]() {
this._unsubscribe();
}
}
export class StatefulAsyncSubscription {
_unsubscribe;
_unsubscribed = false;
constructor(_unsubscribe) {
this._unsubscribe = _unsubscribe;
}
unsubscribe = async () => {
if (this._unsubscribed)
return false;
await this._unsubscribe();
return this._unsubscribed = true;
};
get unsubscribed() {
return this._unsubscribed;
}
async [Symbol.asyncDispose]() {
await this._unsubscribe();
}
}
export class ReplaceableSubscription extends StatefulSubscription {
subscription;
constructor(subscription) {
super(() => {
this.subscription?.();
this.subscription = null;
});
this.subscription = subscription;
}
update(subscription, unsubscribePrevious) {
if (unsubscribePrevious)
this.subscription?.();
this.subscription = subscription;
}
}
export class ReplaceableAsyncSubscription extends StatefulSubscription {
subscription;
constructor(subscription) {
super(() => {
this.subscription?.();
this.subscription = null;
});
this.subscription = subscription;
}
update(subscription, unsubscribePrevious) {
if (unsubscribePrevious)
this.subscription?.();
this.subscription = subscription;
}
}
//# sourceMappingURL=teardown-manager.js.map