Jobs in Bull transition through various states during their lifecycle. These methods allow you to query and manipulate job state.
Status Check Methods
Bull provides convenience methods to check if a job is in a specific state. These methods are more efficient than calling getState() and comparing the result.
isCompleted()
isCompleted (): Promise < boolean >
Returns a promise resolving to true if the job’s state is completed.
const job = await queue . getJob ( jobId );
if ( await job . isCompleted ()) {
console . log ( 'Job completed:' , job . returnvalue );
}
isFailed()
isFailed (): Promise < boolean >
Returns a promise resolving to true if the job’s state is failed.
const job = await queue . getJob ( jobId );
if ( await job . isFailed ()) {
console . log ( 'Job failed:' , job . failedReason );
await job . retry ();
}
isDelayed()
isDelayed (): Promise < boolean >
Returns a promise resolving to true if the job’s state is delayed.
const job = await queue . getJob ( jobId );
if ( await job . isDelayed ()) {
console . log ( 'Job scheduled to run later' );
}
isActive()
isActive (): Promise < boolean >
Returns a promise resolving to true if the job’s state is active (currently being processed).
const job = await queue . getJob ( jobId );
if ( await job . isActive ()) {
console . log ( 'Job is currently processing' );
}
isWaiting()
isWaiting (): Promise < boolean >
Returns a promise resolving to true if the job’s state is waiting (queued for processing).
const job = await queue . getJob ( jobId );
if ( await job . isWaiting ()) {
console . log ( 'Job is in the queue' );
}
isPaused()
isPaused (): Promise < boolean >
Returns a promise resolving to true if the job’s state is paused.
const job = await queue . getJob ( jobId );
if ( await job . isPaused ()) {
console . log ( 'Job is paused' );
}
isStuck()
isStuck (): Promise < boolean >
Returns a promise resolving to true if the job’s state is stuck.
const job = await queue . getJob ( jobId );
if ( await job . isStuck ()) {
console . log ( 'Job is stuck and may need attention' );
}
Example: Comprehensive State Checking
async function checkJobStatus ( jobId ) {
const job = await queue . getJob ( jobId );
if ( await job . isCompleted ()) {
return { status: 'completed' , result: job . returnvalue };
} else if ( await job . isFailed ()) {
return { status: 'failed' , error: job . failedReason };
} else if ( await job . isActive ()) {
return { status: 'processing' , progress: job . progress () };
} else if ( await job . isWaiting ()) {
return { status: 'queued' };
} else if ( await job . isDelayed ()) {
return { status: 'scheduled' };
} else if ( await job . isPaused ()) {
return { status: 'paused' };
} else if ( await job . isStuck ()) {
return { status: 'stuck' };
}
return { status: 'unknown' };
}
getState()
getState (): Promise < JobStatus >
Returns a promise resolving to the current job’s status.
Possible values: completed, failed, delayed, active, waiting, paused, stuck, or null
The implementation of this method is not very efficient, nor is it atomic. If your queue has a very large quantity of jobs, you may want to avoid using this method frequently.
Checking Job State
const job = await queue . getJob ( jobId );
const state = await job . getState ();
console . log ( `Job is currently: ${ state } ` );
if ( state === 'failed' ) {
console . log ( 'Job failed:' , job . failedReason );
await job . retry ();
} else if ( state === 'completed' ) {
console . log ( 'Job completed with result:' , job . returnvalue );
}
State-Based Logic
async function handleJob ( jobId ) {
const job = await queue . getJob ( jobId );
const state = await job . getState ();
switch ( state ) {
case 'waiting' :
case 'delayed' :
console . log ( 'Job is queued' );
break ;
case 'active' :
console . log ( 'Job is currently processing' );
break ;
case 'completed' :
console . log ( 'Job finished successfully' );
break ;
case 'failed' :
console . log ( 'Job failed, retrying...' );
await job . retry ();
break ;
default :
console . log ( 'Unknown state:' , state );
}
}
update()
update ( data : object ): Promise
Updates a job’s data field with the given data object.
The new data object to replace job.data
Updating Job Data
const job = await queue . add ({ status: 'pending' , items: [] });
// Later, update the job data
await job . update ({
status: 'in-progress' ,
items: [ 'item1' , 'item2' , 'item3' ]
});
console . log ( job . data );
// { status: 'in-progress', items: ['item1', 'item2', 'item3'] }
Dynamic Job Updates
queue . process ( async ( job ) => {
// Fetch additional data during processing
const additionalData = await fetchExtraData ( job . data . id );
// Update job with new data
await job . update ({
... job . data ,
additionalData: additionalData
});
// Continue processing with updated data
return processWithData ( job . data );
});
Updating job data does not change the job’s state or restart processing. It only updates the job.data field.
moveToCompleted()
moveToCompleted ( returnValue : any , ignoreLock : boolean , notFetch ?: boolean ): Promise < [ string , string ] | null >
Moves a job to the completed queue. Pulls a job from ‘waiting’ to ‘active’ and returns a tuple containing the next job’s data and id.
The value to set as the job’s return value
Whether to ignore the job lock
Set to true to avoid prefetching the next job in the queue
Tuple of [nextJobData, nextJobId] or null if no job is waiting
This is a low-level method. In most cases, you should return a value from your processor function instead of calling this method directly.
Manual Completion
queue . process ( async ( job ) => {
// Custom processing logic
const result = await customProcess ( job . data );
// Manually move to completed
await job . moveToCompleted ( result , false );
// Note: You typically wouldn't do this - just return the result instead
});
moveToFailed()
moveToFailed ( errorInfo : { message: string }, ignoreLock ?: boolean ): Promise < [ string , string ] | null >
Moves a job to the failed queue. Pulls a job from ‘waiting’ to ‘active’ and returns a tuple containing the next job’s data and id.
Object containing error information
Whether to ignore the job lock
Tuple of [nextJobData, nextJobId] or null if no job is waiting
This is a low-level method. In most cases, you should throw an error from your processor function instead of calling this method directly.
Manual Failure
queue . process ( async ( job ) => {
const validation = validateJob ( job . data );
if ( ! validation . valid ) {
// Manually move to failed
await job . moveToFailed ({
message: `Validation failed: ${ validation . error } `
}, false );
return ;
}
// Continue processing...
// Note: You typically wouldn't do this - just throw an error instead
});
Recommended Approach
Instead of using moveToCompleted() and moveToFailed() directly:
queue . process ( async ( job ) => {
// Validate
if ( ! isValid ( job . data )) {
throw new Error ( 'Invalid job data' );
}
// Process
const result = await processJob ( job . data );
// Return result (Bull handles moveToCompleted internally)
return result ;
});