const Pre = '__pre__' const Last = '__last__' export type Handler = (event: T) => any export type Events = Record export type Flush = { pre?: boolean; last?: boolean } export type Bus = { on( type: Key, handler: Handler, flush?: Flush ): void off( type: Key, handler?: Handler, flush?: Flush ): void emit(type: Key, args?: T[Key]): Promise } export const useAsyncBus = () => { const store = {} as { [key in keyof T]: Array } const allKey = (type): Array => [Pre + type, type, Last + type] const getKey = (type, flush: Flush): keyof T => { const keys = allKey(type) return flush ? flush.pre ? keys[0] : flush.last ? keys[2] : keys[0] : keys[0] } const bus: Bus = { on: (type, handler, flush) => { const key = getKey(type, flush) if (!store[key]) { store[key] = [] } if (!store[key].includes(handler)) { store[key].push(handler) } }, off: (type, handler, flush) => { const keys = flush ? [getKey(type, flush)] : allKey(type) for (const key of keys) { if (handler) { if (store[key]) { store[key] = store[key].filter(h => h !== handler) } } else if (store[key]) { delete store[key] } } }, emit: async (type, args) => { const keys = allKey(type) for (const key of keys) { if (store[key]) { for (const fn of store[key]) { await fn(args) } } } } } return bus }