Just Jake Just Jake - 1 year ago 68
Javascript Question

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:

// 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
. The only problem is, there seems to be no way to do this. Is there a
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 )

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

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).

// 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.


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:

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

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

        h = window.setInterval(function () {
                elapsed += interval;
                if (elapsed < delay) {
                    listener(delay - elapsed);
                } else {
            }, 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.

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.