import { ref } from 'vue' import type { Ref } from 'vue' export interface HandlerContext { data: T itemsToBeProcessed: () => number } 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<(param: { data: T itemsToBeProcessed: () => number }) => Promise> }) { const queue = ref([]) as Ref const isProcessing = ref(false) const internalEventHandler: Events = { add: [], pick: [], processing: [], error: [], processed: [], done: [], } 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 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('processed', payload, result, handler) } catch (err) { emit('error', payload, err as Error, handler) continue } } isProcessing.value = false emit('done', payload) } on('add', handleItem) on('done', handleItem) return { add, on, queue, } }