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

JavaScript倒计时到期后向上计数问题修复请求

修复JavaScript倒计时到期后继续计数的问题

我开发了一个JavaScript倒计时功能,原本设定在目标时间到达后停止计数并执行后续任务,但实际情况是时间到了之后它没停止,反而开始向上计数。

原代码

setInterval(function() {
  $.ajax({
      method: 'get',
      url: 'user/getCurrentPlanAjax',
      dataType: 'json',
      success: function(result) {
          let mining_total = 0;
          let total_balance = 0;
          // $('#total_balance').html("")
          $('#mining_total').html("")
          $('#transaction_current').html("")
          $.each(result, function(e, i) {
              let timeRemaining = '';
              let endTime = new Date(i.ending_time); // Replace with your end time
              let now = new Date();
              let timeDiff = convertTZ(endTime, "America/New_York") - convertTZ(now,
                  "America/New_York");
              console.log(timeDiff, result)
              if (timeDiff <= 0) {
                  timeRemaining = 'Time has expired.';
              } else {
                  let daysRemaining = Math.floor(timeDiff / (1000 * 60 * 60 * 24));
                  let hoursRemaining = Math.floor((timeDiff / (1000 * 60 * 60)) % 24);
                  let minutesRemaining = Math.floor((timeDiff / (1000 * 60)) % 60);
                  let secondsRemaining = Math.floor((timeDiff / 1000) % 60);

                  if (daysRemaining > 0) {
                      timeRemaining += daysRemaining + ' day' + (daysRemaining > 1 ?
                          's ' : ' ');
                  }

                  if (hoursRemaining > 0) {
                      timeRemaining += hoursRemaining + ' hour' + (hoursRemaining >
                          1 ? 's ' : ' ');
                  }

                  if (minutesRemaining > 0) {
                      timeRemaining += minutesRemaining + ' minute' + (
                          minutesRemaining > 1 ? 's ' : ' ');
                  }

                  if (secondsRemaining > 0) {
                      timeRemaining += secondsRemaining + ' second' + (
                          secondsRemaining > 1 ? 's' : '');
                  }
              }

              mining_total += Number(i.total_seconds * i.earning_per_seconds);
              // total_balance = Number(i.balance)

              // if (timeRemaining !== 'Time has expired.') {
              let append = '<tr>' +
                  '<td data-label="Plan Name: ">' + i.plan_name + '</td>' +
                  '<td data-label="Remaining time: ">' +
                  '<span class="base--color">' + i.total_second + '</span>' +
                  '</td>' +
                  '<td data-label="Total earning amount: ">' +
                  '<span class="text-success">' + parseFloat((i.total_mining_seconds *
                      i.earning_per_seconds) - (i.total_seconds * i
                      .earning_per_seconds)).toFixed(8) + '</span>' +
                  '</td>' +
                  '</tr>';

              $('#transaction_current').append(append);
              // }

          })

          $('#mining_total').html(parseFloat(mining_total).toFixed(8));
          // $('#total_balance').html(parseFloat(Number(total_balance) + Number(mining_total)).toFixed(8));

      },
  });
}, 900)

尝试过的无效方法

  • 将mining_total变量声明在setInterval函数外部,避免每次间隔重置为0;
  • 将mining_total的计算移至$.each循环外,使用累积值更新;
  • 添加时间到期检查,停止倒计时并将累积的mining_total值发送至数据库。

尝试后的代码

let mining_total = 0; // Declare mining_total outside setInterval

setInterval(function() {
    $.ajax({
        method: 'get',
        url: 'user/getCurrentPlanAjax',
        dataType: 'json',
        success: function(result) {
            let total_balance = 0;
            // $('#total_balance').html("")
            $('#mining_total').html("");
            $('#transaction_current').html("");

            $.each(result, function(e, i) {
                // Existing code...

                // Calculate mining_total for each item and accumulate
                mining_total += Number(i.total_seconds * i.earning_per_seconds);

                // Existing code...
            });

            if (timeDiff <= 0) {
                // Time has expired
                clearInterval(this); // Stop the countdown

                // Send the accumulated mining_total to the database (total_balance)
                $.ajax({
                    method: 'post',
                    url: 'user/updateTotalBalance',
                    data: {
                        total_balance: mining_total
                    },
                    success: function(response) {
                        // Handle the success response
                    },
                    error: function(error) {
                        // Handle the error
                    }
                });

                return; // Exit the function
            }

            // Update the mining_total and total_balance in the HTML
            $('#mining_total').html(parseFloat(mining_total).toFixed(8));
            // $('#total_balance').html(parseFloat(Number(total_balance) + Number(mining_total)).toFixed(8));

        },
    });
}, 900);

问题根源分析

  1. 定时器引用错误:clearInterval(this)无法生效,因为this在ajax的success回调中指向的是ajax配置对象,不是setInterval返回的定时器ID,必须提前保存ID才能清除。
  2. 变量作用域问题:timeDiff是在$.each循环内部定义的,循环外无法访问,导致到期判断逻辑永远不会触发。
  3. 累积逻辑错误:把mining_total放在setInterval外部会导致每次请求都重复累加旧值,应该基于后端返回的当前数据重新计算,而非持续累积。
  4. 到期后未停止渲染:原代码即使检测到时间到期,仍会继续渲染表格并更新数值,导致出现向上计数的现象。

修复后的完整代码

// 保存定时器ID,用于后续清除
let countdownTimer = setInterval(function() {
  $.ajax({
    method: 'get',
    url: 'user/getCurrentPlanAjax',
    dataType: 'json',
    success: function(result) {
      let mining_total = 0;
      let total_balance = 0;
      let allPlansExpired = true; // 标记是否所有计划都已到期
      
      $('#mining_total').html("");
      $('#transaction_current').html("");

      $.each(result, function(e, i) {
        let timeRemaining = '';
        let endTime = new Date(i.ending_time);
        let now = new Date();
        let timeDiff = convertTZ(endTime, "America/New_York") - convertTZ(now, "America/New_York");
        
        // 只要有一个计划未到期,就标记为未全部到期
        if (timeDiff > 0) {
          allPlansExpired = false;
          let daysRemaining = Math.floor(timeDiff / (1000 * 60 * 60 * 24));
          let hoursRemaining = Math.floor((timeDiff / (1000 * 60 * 60)) % 24);
          let minutesRemaining = Math.floor((timeDiff / (1000 * 60)) % 60);
          let secondsRemaining = Math.floor((timeDiff / 1000) % 60);

          if (daysRemaining > 0) {
            timeRemaining += daysRemaining + ' day' + (daysRemaining > 1 ? 's ' : ' ');
          }
          if (hoursRemaining > 0) {
            timeRemaining += hoursRemaining + ' hour' + (hoursRemaining > 1 ? 's ' : ' ');
          }
          if (minutesRemaining > 0) {
            timeRemaining += minutesRemaining + ' minute' + (minutesRemaining > 1 ? 's ' : ' ');
          }
          if (secondsRemaining > 0) {
            timeRemaining += secondsRemaining + ' second' + (secondsRemaining > 1 ? 's' : '');
          }
        } else {
          timeRemaining = 'Time has expired.';
        }

        // 只计算未到期计划的收益
        if (timeDiff > 0) {
          mining_total += Number(i.total_seconds * i.earning_per_seconds);
        }

        // 渲染表格行
        let append = '<tr>' +
          '<td data-label="Plan Name: ">' + i.plan_name + '</td>' +
          '<td data-label="Remaining time: ">' +
          '<span class="base--color">' + (timeDiff > 0 ? timeRemaining : 'Time has expired.') + '</span>' +
          '</td>' +
          '<td data-label="Total earning amount: ">' +
          '<span class="text-success">' + parseFloat((i.total_mining_seconds * i.earning_per_seconds) - (i.total_seconds * i.earning_per_seconds)).toFixed(8) + '</span>' +
          '</td>' +
          '</tr>';

        $('#transaction_current').append(append);
      });

      // 检查是否所有计划都已到期
      if (allPlansExpired) {
        clearInterval(countdownTimer); // 停止定时器
        // 发送最终收益到数据库
        $.ajax({
          method: 'post',
          url: 'user/updateTotalBalance',
          data: { total_balance: mining_total },
          success: function(response) {
            // 可添加成功后的提示或页面跳转
            console.log('Balance updated successfully');
          },
          error: function(error) {
            console.error('Failed to update balance:', error);
          }
        });
        return;
      }

      // 更新页面显示的收益
      $('#mining_total').html(parseFloat(mining_total).toFixed(8));
    },
    error: function() {
      // 请求失败时可考虑停止定时器或提示用户
      console.error('Failed to fetch plan data');
    }
  });
}, 900);

修复要点说明

  • 保存定时器ID:用countdownTimer变量存储setInterval返回的ID,确保能正确清除定时器。
  • 全局到期判断:新增allPlansExpired变量,遍历所有计划后判断是否全部到期,避免单个计划到期就停止的问题。
  • 修正收益计算:仅计算未到期计划的收益,避免到期后仍累加无效数值。
  • 停止后续执行:到期后调用clearInterval停止定时器,并执行一次数据库更新,不再继续发起请求和渲染页面。
  • 完善错误处理:添加ajax请求失败的处理逻辑,提升代码健壮性。

内容的提问来源于stack exchange,提问作者Onyeka Franklin

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.18 08:24:54