setTimeout全部执行完成后如何给元素赋值?数字递增小数位异常
问题原因与解决方案
核心问题1:执行顺序错误
setTimeout属于异步宏任务,JS执行时会先跑完所有同步代码,再执行异步队列里的任务。你写的直接赋值最终值的代码是同步执行的,会在所有setTimeout回调触发前先运行,后续回调执行时又会把数值覆盖为递增的整数,最终停在小于目标值的最大整数,导致小数位显示为00。
核心问题2:步长设置错误
for循环中你每次给i加1,i全程都是整数,哪怕执行顺序正确也永远达不到带小数的目标值。
最优解决方案(推荐)
使用requestAnimationFrame实现,和浏览器刷新频率同步,动画更流畅,不会生成大量定时器占用性能,天然支持带小数的目标值:
function getTotalIncome() { fetch('/Invoices/GetProviderTotalIncome') .then(res => res.json()) .then(function (data) { // 总动画时长,单位毫秒,可自行调整 const ANIMATION_DURATION = 1500 const startTime = performance.now() const incomeEle = document.getElementById('totalIncome') function updateValue(currentTime) { const elapsedTime = currentTime - startTime // 计算当前动画进度,最大为1 const progress = Math.min(elapsedTime / ANIMATION_DURATION, 1) // 按进度计算当前显示值 const currentValue = progress * data incomeEle.innerHTML = currentValue.toFixed(2) // 未完成动画则继续调用下一帧 if (progress < 1) { requestAnimationFrame(updateValue) } } requestAnimationFrame(updateValue) }) }
HTML代码无需修改。
原有setTimeout写法的修正方案
如果要保留你原有思路,需要调整步长、延迟逻辑,并且把最终值赋值放到最后一个定时器回调里:
function getTotalIncome() { fetch('/Invoices/GetProviderTotalIncome') .then(res => res.json()) .then(function (data) { // 步长调整为0.01,支持小数递增 const step = 0.01 // 每个定时器的执行间隔 const delayPerStep = 1 let timerCount = 0 const incomeEle = document.getElementById('totalIncome') for (let i = 0.0; i <= data; i += step) { setTimeout(function () { incomeEle.innerHTML = i.toFixed(2) // 最后一步赋值精准的目标值,避免步长误差 if (i + step > data) { incomeEle.innerHTML = data.toFixed(2) } }, delayPerStep * timerCount) timerCount++ } }) }
注意:该方案在目标数值较大时会生成大量定时器,性能远低于
requestAnimationFrame方案。
内容的提问来源于stack exchange,提问作者Gabriel Francisco
相关产品推荐
相关产品推荐

