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

