Description
As originally described in the issue #710 the method lastDate() and therefore lastExecution indicate the moment when the last time was checked, if the next execution can be triggered and not when.
In the following section we are able to see that the lastExecution property will be set to the current Date before fireOnTick was even called.
|
// we have arrived at the correct point in time. |
|
this.lastExecution = new Date(); |
|
|
|
this._isActive = false; |
|
|
|
// start before calling back so the callbacks have the ability to stop the cron job |
|
if (!this.runOnce) this.start(); |
|
|
|
void this.fireOnTick(); |
I think this is a bug as the variable name, lastExecution, and method description lastDate: Provides the last execution date. indicate information about the last execution rather the last tick as it's otherwise indicated by the methods
nextDate: Indicates the subsequent date that will activate an onTick.
nextDates(count): Supplies an array of upcoming dates that will initiate an onTick.
I think currently we are seeing more likely the information about the lastTick or lastStart if you will.
Expected Behavior
lastDate() and lastExecution actually represent the moment when the callback(s) where invoked.
Actual Behavior
lastDate() and lastExecution represent the moment of the last check if the callback(s) can be invoked and will be updated on each iteration even without invoking any callback.
Possible Fix
Set lastExecution after setting _isCallbackRunning = true at:
|
this._isCallbackRunning = true; |
Drop setting lastExecution at:
|
this.lastExecution = new Date(); |
|
this.lastExecution = new Date(); |
|
this.lastExecution = new Date(); |
Steps to Reproduce
import { CronJob } from 'cron';
console.log('=== Issue: lastExecution changes while callback is running ===\n');
console.log('Cron: every 5 seconds');
console.log('Callback: takes 60 seconds');
console.log('waitForCompletion: true (should prevent overlapping executions)\n');
let callbackStartTime: Date | null = null;
const callback = async () => {
callbackStartTime = new Date();
console.log(`🔵 Callback STARTED at: ${callbackStartTime.toISOString()}\n`);
await new Promise((resolve) => setTimeout(resolve, 60000)); // 60 seconds
console.log(`🟢 Callback COMPLETED at: ${new Date().toISOString()}\n`);
callbackStartTime = null;
};
const job = new CronJob('*/5 * * * * *', callback, null, true, null, null, false, null, false, true);
// Monitor every 2 seconds
setInterval(() => {
const now = new Date();
const actual = job.lastExecution;
const isRunning = job.isCallbackRunning;
if (!actual) return;
console.log(`[${now.toISOString()}]`);
console.log(` isCallbackRunning: ${isRunning}`);
console.log(` ACTUAL lastExecution: ${actual.toISOString()}`);
if (callbackStartTime) {
console.log(` EXPECTED lastExecution: ${callbackStartTime.toISOString()}`);
const diff = actual.getTime() - callbackStartTime.getTime();
if (Math.abs(diff) > 100) {
console.log(` ❌ DIFFERENCE: ${diff}ms`);
console.log(' ❌ lastExecution changed while callback is STILL RUNNING!');
} else {
console.log(' ✅ lastExecution matches callback start time');
}
}
console.log('');
}, 2000);
setTimeout(() => {
job.stop();
console.log('\n=== Summary ===');
console.log('Problem: lastExecution updates every 5s even though callback is still running');
console.log('Expected: lastExecution should stay constant until callback completes');
console.log('Solution: Set lastExecution in fireOnTick() after isCallbackRunning = true');
process.exit(0);
}, 65000);
Context
We want to monitor our cron's and know if they maybe take longer as they should or they are not invoked anymore besides that the callback is not running.
At least for us it's no solution to work with the threshold and timeout to execute the callback(s) even if its still running.
Your Environment
cron version: 4.4.0
- NodeJS version: 25.7.0
- Operating System and version: Debian 13
- TypeScript version (if applicable): 5.9.3
- Link to your project (if applicable): not possible to provide, sorry
Description
As originally described in the issue #710 the method
lastDate()and thereforelastExecutionindicate the moment when the last time was checked, if the next execution can be triggered and not when.In the following section we are able to see that the
lastExecutionproperty will be set to the currentDatebeforefireOnTickwas even called.node-cron/src/job.ts
Lines 336 to 344 in d6f7c4a
I think this is a bug as the variable name,
lastExecution, and method descriptionlastDate: Provides the last execution date.indicate information about thelast executionrather the lasttickas it's otherwise indicated by the methodsI think currently we are seeing more likely the information about the
lastTickorlastStartif you will.Expected Behavior
lastDate()andlastExecutionactually represent the moment when the callback(s) where invoked.Actual Behavior
lastDate()andlastExecutionrepresent the moment of the last check if the callback(s) can be invoked and will be updated on each iteration even without invoking any callback.Possible Fix
Set
lastExecutionafter setting_isCallbackRunning = trueat:node-cron/src/job.ts
Line 248 in d6f7c4a
Drop setting
lastExecutionat:node-cron/src/job.ts
Line 130 in d6f7c4a
node-cron/src/job.ts
Line 337 in d6f7c4a
node-cron/src/job.ts
Line 367 in d6f7c4a
Steps to Reproduce
Context
We want to monitor our cron's and know if they maybe take longer as they should or they are not invoked anymore besides that the callback is not running.
At least for us it's no solution to work with the
thresholdandtimeoutto execute the callback(s) even if its still running.Your Environment
cronversion: 4.4.0