Javascript setTimeOut 不准确,奇怪的行为

问题描述 投票:0回答:1

我需要在浏览器控制台中进行一些精确的计时。它向另一个使用 pool.ntp.org 的网站发送请求来同步自己的时间。如果是+-50ms就好了。我知道我的 ping 值有所不同,但没关系。我通常有一个相当稳定的 ping 值,大约 25。 我的第一个测试是使用超时功能,但这非常不准确。特别是如果您必须设置更长的超时时间几个小时。 然后我添加了对 https://worldtimeapi.org/api/ip 的调用,以获得更准确的开始时间。我还在实际调用之前 10 秒添加了对该 api 的第二次调用,以重置计时器并更加精确。 现在我的奇怪行为。 我想在 2 小时 10 分钟内执行一个函数。所以我将第一个 timeOut 设置为 2 小时 9 分 50 秒。然后我再次从 Web api 调用当前时间,并从“executeTime-newFetchedTime”设置新的超时。

我记录了所有的差异时间。这有点切中要害。 在实际调用前 10 秒,我得到了新的当前时间。我设置了新的超时并记录新的超时持续时间。

我的问题是,我在执行前10秒查询新时间。我找回时间,设置新的时间。这始终约为 8000-9500 毫秒(因此第一次超时约为 1-2 秒已经关闭)。新的超时完成后,它会执行该函数。但它仍然关闭。它不是由随机的东西造成的,它的关闭有点完全是由第一次超时测量到的差异造成的。

因此,如果我测量第一次和第二次调用之间的时间差为 1450 毫秒,则我的执行时间大约会减少 1400-1500 毫秒。 如果测量的时间差在 800ms 左右,则执行时间为 750-850ms。 所以不知何故,我的代码中有一个错误(虽然我找不到它,这是一个非常简单的代码),或者我的 chrome 浏览器有一些非常奇怪的行为。

这是代码:

const network_delay = 50
//execute the function at 2023.09.18 15:30:35:500
const executeTime = new Date('2023.09.18 15:30:35').getTime() + 500  - network_delay
const fetchTime = executeTime - 10000;

function fetchCurrentTime(callback) {
  fetch('https://worldtimeapi.org/api/ip')
    .then(response => response.json())
    .then(data => {
      const serverTime = new Date(data.utc_datetime).getTime();
      callback(serverTime);
    })
    .catch(error => {
      console.error('Error time API:', error);
    });
}

fetchCurrentTime(serverTime1 => {
    console.log("current time: " + serverTime1)
    console.log("fetch time: " + new Date(fetchTime))
    setTimeout(() => {
      fetchCurrentTime(serverTime2 => {
        console.log("time fetched again. now:" + serverTime2)
        console.log("execute function in: " + (executeTime - serverTime2))
        setTimeout(() => {
            executeFunction();
        }, (executeTime - serverTime2));
      });
    }, fetchTime - serverTime1);
})

我也尝试了动画框架,但这在后台不起作用。 我用网络工作者尝试过它,但由于它位于浏览器控制台中,我必须将代码编写为blod-url才能调用它,但在该代码中我无法访问该文档。但在我的执行函数中,我需要访问该文档。

如果第二次超时正确的话,我对当前的实现会很满意。有人知道如何“解决”这个问题吗?

javascript settimeout
1个回答
0
投票

您也许可以做这样的事情 - 定期测量您的时间偏差(如果您信任该时间提供商)并考虑您的延迟以及浏览器获取该时间所需的时间。

但正如所说,

setTimeout
不能保证非常准确。

    let executeTime = Date.parse('2023-09-18T13:21:35Z');

    async function fetchCurrentTime() {
        const startTime = performance.now();
        const response = await fetch('https://worldtimeapi.org/api/ip');
        const data = await response.json();
        const realTime = new Date(data.utc_datetime).getTime();
        const endTime = performance.now();
        const fetchTime = endTime - startTime;
        return Math.round(realTime + fetchTime / 2); // Assume symmetric latency
    }

    async function tick() {
        const realTime = await fetchCurrentTime();
        const now = Date.now();
        const skew = realTime - now;
        if (now >= executeTime) {
            console.log("Ding!", realTime - executeTime, "ms late");
            executeTime = now + 15000;
        }
        const delay = Math.min(10000, executeTime - now - skew);
        console.log(`${executeTime - now} ms to deadline, skew ${skew} ms, waiting for ${delay} ms`);
        setTimeout(tick, delay);
    }

    tick();
© www.soinside.com 2019 - 2024. All rights reserved.