import { ref } from 'vue' import type { Ref } from 'vue' export interface HandlerContext { data: T itemsToBeProcessed: () => number emit: (eventName: string, ...params: any[]) => void } interface Events { add: Array<(payload: T) => void> pick: Array<(payload: T) => void> processing: Array<(payload: T, handler: (param: HandlerContext) => Promise) => void> error: Array<(payload: T, error: Error, handler: (param: HandlerContext) => Promise) => void> processed: Array<(payload: T, result: R, handler: (param: HandlerContext) => Promise) => void> done: Array<(payload: T) => void> } export function useQueue(options: { handlers: Array<(ctx: HandlerContext) => Promise> }) { const queue = ref([]) as Ref const isProcessing = ref(false) const internalEventHandler: Events = { add: [], pick: [], processing: [], error: [], processed: [], done: [], } const internalHandlerEventHandler: Record void>> = {} function on>(eventName: E, handler: Events[E][number]) { internalEventHandler[eventName].push(handler as any) } function emit>(eventName: E, ...params: Parameters[E][number]>) { const handlers = internalEventHandler[eventName] as Events[E] handlers.forEach((handler) => { (handler as any)(...params) }) } function onHandlerEvent(eventName: string, handler: (...params: any[]) => void) { internalHandlerEventHandler[eventName] = internalHandlerEventHandler[eventName] || [] internalHandlerEventHandler[eventName].push(handler) } function emitHandlerEvent(eventName: string, ...params: any[]) { const handlers = internalHandlerEventHandler[eventName] || [] handlers.forEach((handler) => { handler(...params) }) } async function add(payload: T) { queue.value.push(payload) emit('add', payload) } function pick() { const payload = queue.value.shift() if (!payload) return emit('pick', payload) return payload } async function handleItem() { if (isProcessing.value) return const payload = pick() if (!payload) return isProcessing.value = true for (const handler of options.handlers) { emit('processing', payload, handler) try { const result = await handler({ data: payload, itemsToBeProcessed: () => queue.value.length, emit: emitHandlerEvent }) emit('processed', payload, result, handler) } catch (err) { emit('error', payload, err as Error, handler) continue } } isProcessing.value = false emit('done', payload) // Process next item if any if (queue.value.length > 0) handleItem() } on('add', handleItem) on('done', handleItem) return { add, on, onHandlerEvent, queue, } }