export interface MyAsyncCallback { displayName?: string; (...param: Argument): Promise | undefined | undefined | boolean; } /** * like CallbackList, but async run one by one */ export class AsyncCallbackList { protected list: MyAsyncCallback[] = []; protected running = false; constructor() { this.run = (this.run as any).bind(this); } count() { return this.list.length; } reset() { if (this.running) { throw new Error("Can not reset when it's running."); } this.list.length = 0; } /** * @param name optional name of `item` (will assign displayName to `item`) * @returns function list length */ add(item: MyAsyncCallback): number { if (this.running) { throw new Error("Can not add callback when it's running."); } return this.list.push(item); } /** * @returns if removed: return `item`; if did not exists: return null */ remove(item: MyAsyncCallback): null | MyAsyncCallback { if (this.running) { throw new Error("Can not remove callback when it's running."); } const found = this.list.indexOf(item); if (found !== -1) { // biome-ignore lint/style/noNonNullAssertion: ? return this.list.splice(found, 1)[0]!; } return null; } /** * Stop run if one callback return `true` * @returns {boolean} true if one callback return true */ async run(...argument: Argument): Promise { this.running = true; let ret: boolean | undefined | undefined; for (const cb of this.list) { ret = await cb(...argument); if (ret === true) { break; } } this.running = false; return ret || false; } }