如何在setInterval中返回Promise并访问其数据(含倒计时场景)
如何在倒计时场景中通过Promise获取setInterval的定时计算结果
嘿,我懂你的困扰了!你想每隔1000ms计算一次到指定结束日期的剩余时间,并用Promise的形式拿到结果,但发现直接对setInterval()返回的东西调用.then()完全行不通——这是因为setInterval返回的不是Promise,而是一个数字类型的计时器ID,用来后续清除计时器用的,自然没有Promise的.then()方法。
针对你的倒计时需求,我给你两种实用的解决方案:
方案1:在setInterval回调里直接处理Promise
如果你坚持要用setInterval,可以在每次触发回调的时候创建Promise,然后立刻处理它的结果:
// 先优化一下你的calculateTimeRemaining方法——每次返回新对象避免状态污染 calculateTimeRemaining(endDate: string) { const { secondsInDay, daysOfYear, secondsInHour, secondsInMinute } = this.unitsOfTime; let distance: number = (Date.parse(new Date(endDate).toString()) - Date.parse(new Date().toString())) / this.increment; // 每次创建新的时间对象,避免复用同一个对象导致的状态混乱 const timeRemaining = { years: 0, days: 0, hours: 0, minutes: 0, seconds: 0 }; if (distance > 0) { // 计算剩余年数 if (distance >= daysOfYear * secondsInDay) { timeRemaining.years = Math.floor(distance / (daysOfYear * secondsInDay)); distance -= timeRemaining.years * daysOfYear * secondsInDay; } // 计算剩余天数 if (distance >= secondsInDay) { timeRemaining.days = Math.floor(distance / secondsInDay); distance -= timeRemaining.days * secondsInDay; } // 计算剩余小时数 if (distance >= secondsInHour) { timeRemaining.hours = Math.floor(distance / secondsInHour); distance -= timeRemaining.hours * secondsInHour; } // 计算剩余分钟数 if (distance >= secondsInMinute) { timeRemaining.minutes = Math.floor(distance / secondsInMinute); distance -= timeRemaining.minutes * secondsInMinute; } // 剩余秒数取整,避免小数 timeRemaining.seconds = Math.floor(distance); } return timeRemaining; } // 然后这样用setInterval处理Promise const targetEndDate = "2024-12-31T23:59:59"; const countdownInterval = setInterval(() => { // 封装成Promise并处理结果 new Promise((resolve) => { const remainingTime = this.calculateTimeRemaining(targetEndDate); resolve(remainingTime); }).then((timeData) => { console.log("当前剩余时间:", timeData); // 倒计时结束时清除计时器 if (timeData.years === 0 && timeData.days === 0 && timeData.hours === 0 && timeData.minutes === 0 && timeData.seconds === 0) { clearInterval(countdownInterval); console.log("倒计时完成!"); } }); }, 1000);
方案2:递归setTimeout + Async/Await(更推荐)
其实倒计时场景用递归的setTimeout比setInterval更靠谱——因为setInterval可能会因为前一次执行耗时导致多次回调堆积,而递归setTimeout会在上一次计算完成后再等1秒,避免这个问题。代码也更清晰:
// 封装成异步函数,每次返回剩余时间的Promise async function runCountdown(endDate: string) { const remainingTime = this.calculateTimeRemaining(endDate); // 这里可以直接处理结果,或者返回给外部用.then()接收 console.log("剩余时间更新:", remainingTime); // 还没结束的话,1秒后继续递归 if (remainingTime.years > 0 || remainingTime.days > 0 || remainingTime.hours > 0 || remainingTime.minutes > 0 || remainingTime.seconds > 0) { await new Promise(resolve => setTimeout(resolve, 1000)); await runCountdown(endDate); } else { console.log("倒计时结束啦!"); } } // 启动倒计时 runCountdown("2024-12-31T23:59:59");
如果需要外部通过.then()每次获取更新,可以调整函数让它每次返回Promise:
function runCountdown(endDate: string): Promise<{years: number, days: number, hours: number, minutes: number, seconds: number}> { return new Promise((resolve) => { const remainingTime = this.calculateTimeRemaining(endDate); resolve(remainingTime); // 未结束就继续定时调用 if (remainingTime.years + remainingTime.days + remainingTime.hours + remainingTime.minutes + remainingTime.seconds > 0) { setTimeout(() => runCountdown(endDate).then(resolve), 1000); } }); } // 使用方式 runCountdown("2024-12-31T23:59:59").then((time) => { console.log("最新剩余时间:", time); });
关键提醒
- 别再对
setInterval返回的ID调用.then()啦!那只是个数字,不是Promise。 - 倒计时尽量用递归
setTimeout,避免setInterval的执行堆积问题。 - 计算剩余时间时每次创建新的
timeRemaining对象,不要复用同一个,不然会导致状态混乱。
内容的提问来源于stack exchange,提问作者Frederick M. Rogers
相关产品推荐
相关产品推荐

