查找setTimeout()中剩下的时间?

[英]find the time left in a setTimeout()?


I'm writing some Javascript that interacts with library code that I don't own, and can't (reasonably) change. It creates Javascript timeouts used for showing the next question in a series of time-limited questions. This isn't real code because it is obfuscated beyond all hope. Here's what the library is doing:

我正在编写一些Javascript,这些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 );

I want to put a progress bar onscreen that fills towards questionTime * 1000 by interrogating the timer created by setTimeout. The only problem is, there seems to be no way to do this. Is there a getTimeout function that I'm missing? The only information on Javascript timeouts that I can find is related only to creation via setTimeout( function, time) and deletion via clearTimeout( id ).

我想在屏幕上放置一个进度条,通过询问setTimeout创建的计时器来填充questionTime * 1000。唯一的问题是,似乎没有办法做到这一点。有一个getTimeout函数我漏掉了吗?我能找到的关于Javascript超时的唯一信息只与通过setTimeout(函数,时间)创建和通过clearTimeout(id)删除有关。

I'm looking for a function that returns either the time remaining before a timeout fires, or the time elapsed after a timeout has been called. My progress bar code looks like this:

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

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: How do I find the time remaining before a javascript setTimeout()?

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


Here's the solution I'm using now. I went through the library section that's in charge of tests, and unscrambled the code (terrible, and against my permissions).

这是我现在用的方法。我浏览了负责测试的library部分,并对代码进行了解密(很糟糕,而且违反了我的权限)。

// 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 );

and here's my code:

这是我的代码:

// wrapper for setTimeout
function mySetTimeout( func, timeout ) {
    timeouts[ n = setTimeout( func, timeout ) ] = {
        start: new Date().getTime(),
        end: new Date().getTime() + timeout
        t: timeout
    }
    return n;
}

This works pretty spot-on in any browser that isn't IE 6. Even the original iPhone, where I expected things to get asynchronous.

这在IE 6之外的任何浏览器中都很有效。甚至是最初的iPhone,我希望它能实现异步。

10 个解决方案

#1


24  

If you can't modify the library code, you'll need to redefine setTimeout to suit your purposes. Here's an example of what you could do:

如果不能修改库代码,则需要重新定义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);

This is not production code, but it should put you on the right track. Note that you can only bind one listener per timeout. I haven't done extensive testing with this, but it works in Firebug.

这不是生产代码,但它应该使您走上正确的轨道。注意,每次超时只能绑定一个侦听器。我还没有做过大量的测试,但是它在Firebug中工作。

A more robust solution would use the same technique of wrapping setTimeout, but instead use a map from the returned timeoutId to listeners to handle multiple listeners per timeout. You might also consider wrapping clearTimeout so you can detach your listener if the timeout is cleared.

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

#2


47  

Just for the record, there is a way to get the time left in node.js:

仅就记录而言,有一种方法可以在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);
}

Prints:

打印:

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

This doesn't seem to work in firefox through, but since node.js is javascript, I thought this remark might be helpful for people looking for the node solution.

这在firefox中似乎行不通,但是从node开始。js是javascript,我认为这句话可能对寻找节点解决方案的人有帮助。

#3


40  

EDIT: I actually think I made an even better one: https://stackoverflow.com/a/36389263/2378102

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

I wrote this function and I use it a lot:

我写了这个函数,我经常用它:

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()
}

Make a timer:

定时器:

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

So if you want the time remaining just do:

所以如果你想要剩下的时间,只要:

a.getTimeLeft()

#4


3  

Javascript's event stacks don't operate how you would think.

Javascript的事件堆栈不能按照您的想法操作。

When a timeout event is created, it is added to the event queue, but other events may take priority while that event is being fired, delay the execution time and postponing runtime.

当创建超时事件时,会将其添加到事件队列中,但是在触发该事件时,其他事件可能具有优先级,延迟执行时间和延迟运行时。

Example: You create a timeout with a delay of 10 seconds to alert something to the screen. It will be added to the event stack and will be executed after all current events are fired (causing some delay). Then, when the timeout is processed, the browser still continues to capture other events add them to the stack, which causes further delays in the processing. If the user clicks, or does a lot of ctrl+typing, their events take priority over the current stack. Your 10 seconds can turn into 15 seconds, or longer.

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


That being said, there are many ways to fake how much time has passed. One way is to execute a setInterval right after you add the setTimeout to the stack.

也就是说,有很多方法可以假装时间已经过去了多少。一种方法是在向堆栈添加setTimeout之后立即执行setInterval。

Example: Perform a settimeout with a 10 second delay (store that delay in a global). Then perform a setInterval that runs every second to subtract 1 from the delay and output the delay remaining. Because of how the event stack can influence actual time (described above), this still won't be accurate, but does give a count.

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


In short, there is no real way to get the remaining time. There are only ways to try and convey an estimate to the user.

简而言之,没有真正的方法来获得剩余的时间。只有一些方法可以尝试并向用户传达评估。

#5


1  

Here is maybe an even better way to do it, plus, you won't need to change code you've already written:

这里有一个更好的方法,另外,你不需要改变你已经写过的代码:

var getTimeout = (function() { // IIFE
    var _setTimeout = setTimeout, // Reference to the original setTimeout
        map = {}; // Map of all timeouts with their start date and delay

    setTimeout = function(callback, delay) { // Modify setTimeout
        var id = _setTimeout(callback, delay); // Run the original, and store the id

        map[id] = [Date.now(), delay]; // Store the start date and delay

        return id; // Return the id
    };

    return function(id) { // The actual getTimeLeft function
        var m = map[id]; // Find the timeout in map

        // If there was no timeout with that id, return NaN, otherwise, return the time left clamped to 0
        return m ? Math.max(m[1] - Date.now() + m[0], 0) : NaN;
    }
})();

... And mimimized:

…和最小化:

var getTimeout=function(){var e=setTimeout,b={};setTimeout=function(a,c){var d=e(a,c);b[d]=[Date.now(),c];return d};return function(a){return(a=b[a])?Math.max(a[1]-Date.now()+a[0],0):NaN}}();

#6


0  

No, but you can have your own setTimeout/setInterval for animation in your function.

没有,但是您可以在函数中为动画设置自己的setTimeout/setInterval。

Say your question looks like this:

假设你的问题是这样的:

function myQuestion() {
  // animate the progress bar for 1 sec
  animate( "progressbar", 1000 );

  // do the question stuff
  // ...
}

And your animation will be handled by these 2 functions:

你的动画将由这两个功能来处理:

function interpolate( start, end, pos ) {
  return start + ( pos * (end - start) );
}

function animate( dom, interval, delay ) {

      interval = interval || 1000;
      delay    = delay    || 10;

  var start    = Number(new Date());

  if ( typeof dom === "string" ) {
    dom = document.getElementById( dom );
  }

  function step() {

    var now     = Number(new Date()),
        elapsed = now - start,
        pos     = elapsed / interval,
        value   = ~~interpolate( 0, 500, pos ); // 0-500px (progress bar)

    dom.style.width = value + "px";

    if ( elapsed < interval )
      setTimeout( step, delay );
  }

  setTimeout( step, delay );
}

#7


0  

If anyone's looking back on this. I've come out with a timeout and interval manager that can get you the time left in a timeout or interval as well as do some other stuff. I'll be adding to it to make it more nifty and more accurate, but it seems to work fairly well as is (although I have some more ideas to make it even more accurate):

如果有人回头看的话。我推出了一个超时和间隔管理器,它可以让你在超时或间隔中获得剩余的时间,还可以做一些其他的事情。我将添加它以使它更俏皮和更准确,但它似乎工作得相当不错(尽管我有更多的想法使它更准确):

https://github.com/vhmth/Tock

https://github.com/vhmth/Tock

#8


0  

Question has already been answered but I will add my bit. It just occured to me.

问题已经得到了回答,但我要补充一点。我突然想到。

Use setTimeout in recursion as follows:

在递归中使用setTimeout:

var count = -1;

function beginTimer()
{
    console.log("Counting 20 seconds");
    count++;

    if(count <20)
    {
        console.log(20-count+"seconds left");
        setTimeout(beginTimer,2000);
    }
    else
    {
        endTimer();
    }
}

function endTimer()
{
    console.log("Time is finished");
}

I guess the code is self explanatory

我想代码是不言自明的

#9


0  

Check this one:

检查这一个:

class Timer {
  constructor(fun,delay) {
    this.timer=setTimeout(fun, delay)
    this.stamp=new Date()
  }
  get(){return ((this.timer._idleTimeout - (new Date-this.stamp))/1000) }
  clear(){return (this.stamp=null, clearTimeout(this.timer))}
}

Make a timer:

定时器:

let smtg = new Timer(()=>{do()}, 3000})

Get remain:

保持:

smth.get()

Clear timeout

清晰的超时

smth.clear()

#10


0  

    (function(){
        window.activeCountdowns = [];
        window.setCountdown = function (code, delay, callback, interval) {
            var timeout = delay;
            var timeoutId = setTimeout(function(){
                clearCountdown(timeoutId);
                return code();
            }, delay);
            window.activeCountdowns.push(timeoutId);
            setTimeout(function countdown(){
                var key = window.activeCountdowns.indexOf(timeoutId);
                if (key < 0) return;
                timeout -= interval;
                setTimeout(countdown, interval);
                return callback(timeout);
            }, interval);
            return timeoutId;
        };
        window.clearCountdown = function (timeoutId) {
            clearTimeout(timeoutId);
            var key = window.activeCountdowns.indexOf(timeoutId);
            if (key < 0) return;
            window.activeCountdowns.splice(key, 1);
        };
    })();

    //example
    var t = setCountdown(function () {
        console.log('done');
    }, 15000, function (i) {
        console.log(i / 1000);
    }, 1000);

注意!

本站翻译的文章,版权归属于本站,未经许可禁止转摘,转摘请注明本文地址:https://www.itdaan.com/blog/2010/06/29/4f50f514f6d5c6e8b044d2a98bc91956.html



 
粤ICP备14056181号  © 2014-2020 ITdaan.com