node-schedule回调递归调用父函数致应用崩溃排查求助
Let me break down what's happening here and how to fix it:
When you set up a new timer directly inside the callback of schedule.scheduleJob, you run two critical risks:
- Synchronous nested call stacks: If the callback triggers immediately (e.g., if your sunset time calculation is off and returns a past timestamp), you’ll end up in an infinite recursive loop that clogs the call stack over time.
- Unmanaged timer instances: Every time you call
scheduleSunTimerwithout cleaning up the previous timer, you leave old timer references hanging around. These accumulate in memory, leading to leaks that eventually crash your app after a week or so.
Here’s the step-by-step fix:
Track and cancel old timers first
Always keep a reference to your active timer, and cancel it before creating a new one. This prevents duplicate timers from piling up:let activeSunsetTimer = null; function scheduleSunTimer() { // Clean up existing timer if it exists if (activeSunsetTimer) { activeSunsetTimer.cancel(); } // Calculate the NEXT valid sunset time (more on this below) const nextSunset = getValidNextSunset(); // Schedule the new timer activeSunsetTimer = schedule.scheduleJob(nextSunset, () => { // Run your sunset logic here executeSunsetTasks(); // Trigger next schedule ASYNCHRONOUSLY to avoid call stack bloat process.nextTick(scheduleSunTimer); }); }Ensure your sunset time calculation returns a future timestamp
This is the most common hidden issue. If your calculation accidentally returns a time that’s already passed (e.g., you forget to check if today’s sunset is over), node-schedule will fire the callback immediately—kicking off that infinite loop. Add validation:function getValidNextSunset() { const now = new Date(); let sunsetToday = calculateSunsetForDate(now); // If today's sunset is already done, grab tomorrow's instead if (sunsetToday <= now) { const tomorrow = new Date(now); tomorrow.setDate(tomorrow.getDate() + 1); sunsetToday = calculateSunsetForDate(tomorrow); } return sunsetToday; } // Replace this with your actual sunset calculation logic function calculateSunsetForDate(date) { // Your existing code to get sunset time for the given date }Use async scheduling for the next timer
WrappingscheduleSunTimerinprocess.nextTickpushes the next schedule call to the next iteration of the Node.js event loop. This prevents the call stack from growing indefinitely with nested synchronous calls.
Bonus: Debugging Memory Leaks
To confirm this fixes the leak, you can:
- Run your app with
node --expose-gc app.js, then periodically callglobal.gc()in your code (or via a debug tool) and track memory usage withprocess.memoryUsage(). - Use tools like
clinic.jsto take memory snapshots and check for accumulated timer instances.
内容的提问来源于stack exchange,提问作者Bas van Dijk

