@stacksjs/queue
Version:
The Stacks Queue system.
199 lines (168 loc) • 4.54 kB
TypeScript
import type { QueueOption } from '@stacksjs/types';
declare const queueDriver: 'database';
declare interface JobConfig {
handle?: (payload?: any) => Promise<void>
action?: string | (() => Promise<void>)
}
declare interface Dispatchable {
dispatch: () => Promise<void>
dispatchNow: () => Promise<void>
delay: (seconds: number) => this
afterResponse: () => this
chain: (jobs: Dispatchable[]) => this
onQueue: (queue: string) => this
}
export declare function runJob(name: string, options: QueueOption): Promise<void>;
export declare class Queue implements Dispatchable {
protected options: QueueOption = {}
constructor(
protected name: string,
protected payload?: any,
) { }
async dispatch(): Promise<void> {
const queueName = this.options.queue || 'default'
const jobPayload = this.createJobPayload(queueName)
if (this.isQueuedDriver()) {
await this.storeQueuedJob(jobPayload)
return
}
if (this.options.afterResponse) {
this.deferAfterResponse()
return
}
if (this.options.delay) {
this.deferWithDelay()
return
}
await this.runJobImmediately(jobPayload)
}
private isQueuedDriver(): boolean {
return ['database', 'redis'].includes(queueDriver)
}
private createJobPayload(queueName: string): QueueOption {
return {
queue: queueName,
payload: this.payload,
context: this.options.context,
maxTries: this.options.maxTries,
timeout: this.options.timeout,
backoff: this.options.backoff,
delay: this.options.delay,
}
}
private async storeQueuedJob(jobPayload: any): Promise<void> {
await storeJob(this.name, jobPayload)
}
private deferAfterResponse(): void {
process.on('beforeExit', async () => {
await this.dispatchNow()
})
}
private deferWithDelay(): void {
setTimeout(async () => {
await this.dispatchNow()
}, this.options.delay || 0)
}
private async runJobImmediately(jobPayload: any): Promise<void> {
try {
await runJob(this.name, jobPayload)
await this.runChainedJobs()
}
catch (error) {
log.error(`Failed to dispatch job ${this.name}:`, error)
throw error
}
}
private async runChainedJobs(): Promise<void> {
if (!this.options.chainedJobs?.length)
return
for (const job of this.options.chainedJobs) {
await job.dispatch()
}
}
async dispatchNow(): Promise<void> {
try {
await runJob(this.name, {
payload: this.payload,
context: this.options.context,
immediate: true,
})
if (this.options.chainedJobs?.length) {
for (const job of this.options.chainedJobs) {
await job.dispatchNow()
}
}
}
catch (error) {
log.error(`Failed to execute job ${this.name}:`, error)
throw error
}
}
delay(seconds: number): this {
this.options.delay = seconds
return this
}
onQueue(queue: string): this {
this.options.queue = queue
return this
}
chain(jobs: Dispatchable[]): this {
this.options.chainedJobs = jobs
return this
}
afterResponse(): this {
this.options.afterResponse = true
return this
}
tries(count: number): this {
this.options.maxTries = count
return this
}
timeout(seconds: number): this {
this.options.timeout = seconds
return this
}
backoff(attempts: number[]): this {
this.options.backoff = attempts
return this
}
withContext(context: any): this {
this.options.context = context
return this
}
}
export declare class JobFactory {
static make(name: string, payload?: any): Queue {
return new Queue(name, payload)
}
static dispatch(name: string, payload?: any): Queue {
const job = new Queue(name, payload)
job.dispatch()
return job
}
static async dispatchNow(name: string, payload?: any): Promise<void> {
await new Queue(name, payload).dispatchNow()
}
static later(delay: number, name: string, payload?: any): Queue {
return new Queue(name, payload).delay(delay)
}
static chain(jobs: Queue[]): Queue {
const firstJob = jobs[0]
return firstJob.chain(jobs.slice(1))
}
}
export declare function job(name: string, payload?: any): Queue;
export declare function processOrder(payload, context): void;
export default {
async handle() {
}
};
export default {
action: 'SendWelcomeEmail'
};
export default {
action: async () => {
}
};
export default async function(payload, context) {
};