在 setTimeout() 中找到剩余的时间?

IT技术 javascript timeout settimeout
2021-03-05 11:32:30

我正在编写一些与我不拥有的库代码交互的 Javascript,并且不能(合理地)更改。它创建 Javascript 超时,用于显示一系列限时问题中的下一个问题。这不是真正的代码,因为它被混淆得令人望而却步。这是图书馆正在做的事情:

....
// setup a timeout to go to the next question based on user-supplied time
var t = questionTime * 1000
test.currentTimeout = setTimeout( showNextQuestion(questions[i+1]), t );

我想在屏幕上放置一个进度条,questionTime * 1000通过询问由setTimeout. 唯一的问题是,似乎没有办法做到这一点。是否有getTimeout我缺少功能?我能找到的有关 Javascript 超时的唯一信息仅setTimeout( function, time)与通过clearTimeout( id ).

我正在寻找一个函数,该函数返回超时触发前的剩余时间,或调用超时后经过的时间。我的进度条代码如下所示:

var  timeleft = getTimeout( test.currentTimeout ); // I don't know how to do this
var  $bar = $('.control .bar');
while ( timeleft > 1 ) {
    $bar.width(timeleft / test.defaultQuestionTime * 1000);
}

tl; dr:我如何找到 javascript setTimeout() 之前的剩余时间?


这是我现在使用的解决方案。我浏览了负责测试的库部分,并解读了代码(很糟糕,而且违反了我的权限)。

// setup a timeout to go to the next question based on user-supplied time
var t = questionTime * 1000
test.currentTimeout = mySetTimeout( showNextQuestion(questions[i+1]), t );

这是我的代码:

// setTimeout 的包装器
函数 mySetTimeout(函数,超时){
    超时[ n = setTimeout( func, timeout ) ] = {
        开始:新日期()。getTime(),
        结束:新日期()。getTime()+超时
        t:超时
    }
    返回 n;
}

这在不是 IE 6 的任何浏览器中都非常有效。即使是原始的 iPhone,我希望事情会变得异步。

6个回答

只是为了记录,有一种方法可以在 node.js 中获得剩余时间:

var timeout = setTimeout(function() {}, 3600 * 1000);

setInterval(function() {
    console.log('Time left: '+getTimeLeft(timeout)+'s');
}, 2000);

function getTimeLeft(timeout) {
    return Math.ceil((timeout._idleStart + timeout._idleTimeout - Date.now()) / 1000);
}

印刷:

$ node test.js 
Time left: 3599s
Time left: 3597s
Time left: 3595s
Time left: 3593s

这在 firefox 中似乎不起作用,但由于 node.js 是 javascript,我认为这句话可能对寻找 node 解决方案的人有所帮助。

不确定它是节点的新版本还是什么,但为了使其正常工作,我必须删除“getTime()”部分。似乎 _idleStart 现在已经是一个整数
2021-04-21 11:32:30
我刚在node 0.10试过了,getTime()被去掉了,_idleStart已经是时候了
2021-05-04 11:32:30
不适用于节点 6.3,因为超时结构是这些信息的泄漏。
2021-05-16 11:32:30

编辑:我实际上认为我做了一个更好的:https : //stackoverflow.com/a/36389263/2378102

我写了这个函数并且我经常使用它:

function timer(callback, delay) {
    var id, started, remaining = delay, running

    this.start = function() {
        running = true
        started = new Date()
        id = setTimeout(callback, remaining)
    }

    this.pause = function() {
        running = false
        clearTimeout(id)
        remaining -= new Date() - started
    }

    this.getTimeLeft = function() {
        if (running) {
            this.pause()
            this.start()
        }

        return remaining
    }

    this.getStateRunning = function() {
        return running
    }

    this.start()
}

做一个计时器:

a = new timer(function() {
    // What ever
}, 3000)

因此,如果您想要剩余的时间,请执行以下操作:

a.getTimeLeft()
感谢那。不完全是我一直在寻找的,但是计算剩余时间的功能非常有用
2021-05-13 11:32:30

如果您无法修改库代码,则需要重新定义 setTimeout 以适合您的目的。以下是您可以执行的操作的示例:

(function () {
var nativeSetTimeout = window.setTimeout;

window.bindTimeout = function (listener, interval) {
    function setTimeout(code, delay) {
        var elapsed = 0,
            h;

        h = window.setInterval(function () {
                elapsed += interval;
                if (elapsed < delay) {
                    listener(delay - elapsed);
                } else {
                    window.clearInterval(h);
                }
            }, interval);
        return nativeSetTimeout(code, delay);
    }

    window.setTimeout = setTimeout;
    setTimeout._native = nativeSetTimeout;
};
}());
window.bindTimeout(function (t) {console.log(t + "ms remaining");}, 100);
window.setTimeout(function () {console.log("All done.");}, 1000);

这不是生产代码,但它应该让您走上正轨。请注意,每次超时只能绑定一个侦听器。我没有对此进行广泛的测试,但它在 Firebug 中有效。

更健壮的解决方案将使用包装 setTimeout 的相同技术,而是使用从返回的 timeoutId 到侦听器的映射来处理每个超时的多个侦听器。您也可以考虑包装 clearTimeout 以便在超时被清除时分离您的侦听器。

由于执行时间的原因,这可能会在一段时间内漂移几毫秒。一种更安全的方法是记录开始超时的时间(new Date()),然后从当前时间中减去它(另一个 new Date())
2021-05-06 11:32:30
谢谢你拯救了我的一天!
2021-05-11 11:32:30

服务器端 Node.js 特定

以上都不对我有用,在检查超时对象后,看起来一切都与进程开始时有关。以下对我有用:

myTimer = setTimeout(function a(){console.log('Timer executed')},15000);

function getTimeLeft(timeout){
  console.log(Math.ceil((timeout._idleStart + timeout._idleTimeout)/1000 - process.uptime()));
}

setInterval(getTimeLeft,1000,myTimer);

输出:

14
...
3
2
1
Timer executed
-0
-1
...

node -v
v9.11.1

为简洁起见编辑了输出,但此基本函数给出了执行前或执行后的大致时间。正如其他人提到的,由于节点处理的方式,这一切都不是准确的,但是如果我想抑制不到 1 分钟前运行的请求,并且我存储了计时器,我不明白为什么这不会作为快速检查工作。在 10.2+ 中使用 refreshtimer 处理对象可能会很有趣。

我得到的只是:找不到名称“进程”。
2021-05-02 11:32:30

Javascript 的事件堆栈不会按照您的想法运行。

创建超时事件后,它会被添加到事件队列中,但其他事件可能会在触发该事件时优先,从而延迟执行时间并推迟运行时间。

示例: 您创建了一个延迟 10 秒的超时来向屏幕发出警报。它将被添加到事件堆栈中,并将在所有当前事件被触发后执行(导致一些延迟)。然后,在处理超时时,浏览器仍然继续捕获其他事件将它们添加到堆栈中,从而导致处理中的进一步延迟。如果用户单击或执行大量 ctrl+typing,则他们的事件优先于当前堆栈。您的 10 秒可以变成 15 秒或更长。


话虽如此,有很多方法可以伪造已经过去了多少时间。一种方法是在将 setTimeout 添加到堆栈后立即执行 setInterval。

示例:执行具有 10 秒延迟的设置超时(将该延迟存储在全局中)。然后执行每秒运行一次的 setInterval 以从延迟中减去 1 并输出剩余的延迟。由于事件堆栈如何影响实际时间(如上所述),这仍然不准确,但确实给出了计数。


总之,没有真正的方法来获得剩余时间。只有尝试向用户传达估计的方法。