JavaScript/jQuery中setInterval获取数据总是滞后一次的求助
setInterval获取服务器时间滞后的问题与解决
问题描述
我认为JavaScript中的setInterval()应该在触发间隔的时刻正常工作,但测试发现每次返回的都是上一次间隔的数据。而setTimeout()在每次页面刷新后都能正常工作。
以下函数在客户端移动端从远程服务器获取显示在div中的时间:setTimeout()获取的结果基本正确,客户端与服务器时间一致,存在网络延迟属于正常情况,但无法理解为何setInterval()的结果总是滞后一个间隔。
测试代码
客户端代码
<script> var href = 'https://some-domain.com'; function check_time() { $('#timestamp_far').load(''+href+' #timestamp_far'); setTimeout(function () { element_stamp = document.getElementById('timestamp_far'); stamp_ne = element_stamp.innerText; alert(stamp_ne); }, 500); } // end check_time setInterval(check_time, 30*1000); setTimeout(check_time, 500); </script>
服务器端PHP代码
<?php $date_now_server = date('i:s'); ?> <div id="timestamp_far"> <?php echo $date_now_server; ?></div>
观察现象
- 客户端当前时间10:10时,
setTimeout返回10:12(网络延迟,属正常),setInterval无输出; - 客户端当前时间10:40时,
setTimeout返回10:45(网络延迟,属正常),setInterval返回10:12(始终显示上一次的时间)。
问题原因
核心问题是你把异步的load()请求和固定延迟的setTimeout绑定在了一起,逻辑存在漏洞:
load()是AJAX异步请求,它的完成时间完全取决于网络状况,不是固定的500ms。- 你每次调用
check_time时,先发起请求,然后不管请求是否成功返回,都固定500ms后去读DOM。如果某次网络卡顿,请求耗时超过500ms,那此时读到的还是上一次请求留下的旧数据。 - 后续
setInterval每隔30s触发一次新请求,但只要某次请求的耗时超过500ms,就会一直读到之前的旧内容,自然出现“滞后一个间隔”的现象。
首次的setTimeout(check_time,500)能正常工作,是因为页面刚加载时没有旧数据,第一次请求大概率会在1000ms内完成,刚好能读到最新内容。
解决方案
不要用固定延迟的setTimeout读取DOM,而是利用load()的回调函数,在请求成功并更新DOM后立即读取数据,这才是可靠的方式。
修改后的客户端代码
<script> var href = 'https://some-domain.com'; function check_time() { // 利用load的回调函数,在请求完成、DOM更新后执行读取操作 $('#timestamp_far').load(href + ' #timestamp_far', function() { var element_stamp = document.getElementById('timestamp_far'); var stamp_ne = element_stamp.innerText; alert(stamp_ne); }); } // end check_time setInterval(check_time, 30*1000); setTimeout(check_time, 500); </script>
改进说明
load()的第二个参数是回调函数,只有当AJAX请求成功、目标DOM内容更新完成后才会执行,确保你读取的是最新的服务器返回数据。- 去掉固定的500ms延迟,彻底避免了网络波动导致的旧数据读取问题。
- 给变量加上
var关键字,避免全局变量污染。
替代方案(更直观)
你也可以用$.get直接获取服务器HTML,再提取目标内容,逻辑更清晰:
<script> var href = 'https://some-domain.com'; function check_time() { $.get(href, function(data) { // 从返回的HTML中提取服务器时间 var serverTime = $(data).find('#timestamp_far').text().trim(); // 更新页面显示 $('#timestamp_far').text(serverTime); alert(serverTime); }); } // end check_time setInterval(check_time, 30*1000); setTimeout(check_time, 500); </script>
这种方式不依赖DOM自动替换,直接处理返回数据,能避免多次load可能带来的潜在冲突。
内容的提问来源于stack exchange,提问作者Shahram Mohseni
相关产品推荐
相关产品推荐

