export type Deferred<T> = ReturnType<typeof Promise.withResolvers<T>>;
export class DeferredStack<T> { #elements: Array<T>; #creator?: () => Promise<T>; #max_size: number; #queue: Array<Deferred<T>>; #size: number;
constructor(max?: number, ls?: Iterable<T>, creator?: () => Promise<T>) { this.#elements = ls ? [...ls] : []; this.#creator = creator; this.#max_size = max || 10; this.#queue = []; this.#size = this.#elements.length; }
get available(): number { return this.#elements.length; }
async pop(): Promise<T> { if (this.#elements.length > 0) { return this.#elements.pop()!; } else if (this.#size < this.#max_size && this.#creator) { this.#size++; return await this.#creator(); } const d = Promise.withResolvers<T>(); this.#queue.push(d); return await d.promise; }
push(value: T): void { if (this.#queue.length > 0) { const d = this.#queue.shift()!; d.resolve(value); } else { this.#elements.push(value); } }
get size(): number { return this.#size; }}
export class DeferredAccessStack<T> { #elements: Array<T>; #initializeElement: (element: T) => Promise<void>; #checkElementInitialization: (element: T) => Promise<boolean> | boolean; #queue: Array<Deferred<T>>; #size: number;
get available(): number { return this.#elements.length; }
get size(): number { return this.#size; }
constructor( elements: T[], initCallback: (element: T) => Promise<void>, checkInitCallback: (element: T) => Promise<boolean> | boolean, ) { this.#checkElementInitialization = checkInitCallback; this.#elements = elements; this.#initializeElement = initCallback; this.#queue = []; this.#size = elements.length; }
async initialized(): Promise<number> { const initialized = await Promise.all( this.#elements.map((e) => this.#checkElementInitialization(e)), );
return initialized.filter((initialized) => initialized === true).length; }
async pop(): Promise<T> { let element: T; if (this.available > 0) { element = this.#elements.pop()!; } else { const d = Promise.withResolvers<T>(); this.#queue.push(d); element = await d.promise; }
if (!(await this.#checkElementInitialization(element))) { await this.#initializeElement(element); } return element; }
push(value: T): void { if (this.#queue.length > 0) { const d = this.#queue.shift()!; d.resolve(value); } else { this.#elements.push(value); } }}