Monday, June 3, 2024
 Popular · Latest · Hot · Upcoming
107
rated 0 times [  109] [ 2]  / answers: 1 / hits: 96508  / 14 Years ago, tue, june 29, 2010, 12:00:00

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



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.


More From » timeout

 Answers
13

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;

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.



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.


[#96383] Friday, June 25, 2010, 14 Years  [reply] [flag answer]
Only authorized users can answer the question. Please sign in first, or register a free account.
alanisannettep

Total Points: 695
Total Questions: 96
Total Answers: 91

Location: Australia
Member since Sat, May 27, 2023
1 Year ago
;