You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

node-schedule回调递归调用父函数致应用崩溃排查求助

Fixing Node-Schedule Sunset Timer Hang & Memory Leak Issues

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 scheduleSunTimer without 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:

  1. 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);
      });
    }
    
  2. 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
    }
    
  3. Use async scheduling for the next timer
    Wrapping scheduleSunTimer in process.nextTick pushes 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 call global.gc() in your code (or via a debug tool) and track memory usage with process.memoryUsage().
  • Use tools like clinic.js to take memory snapshots and check for accumulated timer instances.

内容的提问来源于stack exchange,提问作者Bas van Dijk

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.20 08:08:47