views:

213

answers:

5

The window.setTimeout (and related setInterval) function in Javascript allows you to schedule a function to be executed sometime in the future:

id = setTimeout(function, delay);

where "delay" is the number of milliseconds into the future at which you want to have the function called. Before this time elapses, you can cancel the timer using:

clearTimeout(id);

What I want is to update the timer. I want to be able to advance or retard a timer so that the function gets called x milliseconds sooner or later than originally scheduled.

If there were a getTimeout method, you could do something like:

originally_scheduled_time = getTimeout(id);
updateTimeout(id, originally_schedule_time + new_delay);  // change the time

but as far as I can tell there's nothing like getTimeout or any way to update an existing timer.

Is there a way to access the list of scheduled alarms and modify them?

Is there a better approach?

thanks!

A: 

One possibility can be like this:

if (this condition true)
{
  setTimeout(function, 5000);
}
elseif (this condition true)
{
  setTimeout(function, 10000);
}
else
{
  setTimeout(function, 1000);
}

It's up to your how you construct your conditions or the logic. thanks

Sarfraz
+4  A: 

I believe not. A better approach might be to write your own wrapper which stores your timers (func-ref, delay, and timestamp). That way you can pretend to update a timer by clearing it and calculate a copy with an updated delay.

jensgram
@jensgram: an example of that would be appreciated :)
Sarfraz
+1 - I would have posted sooner but I was writing an example ;)
gnarf
Thanks - this does seem to be the answer. Funny that the {set,clear}{Timeout,Interval} API isn't richer.
nibot
+4  A: 

If you really want this sort of functionality, you're going to need to write it yourself.

You could create a wrapper for the setTimeout call, that will return an object you can use to "postpone" the timer:

function setAdvancedTimer(f, delay) {
  var obj = {
    firetime: delay + (+new Date()), // the extra + turns the date into an int 
    called: false,
    canceled: false,
    callback: f
  }; 
  // this function will set obj.called, and then call the function whenever
  // the timeout eventually fires.
  var callfunc = function() { obj.called = true; f(); }; 
  // calling .extend(1000) will add 1000ms to the time and reset the timeout.
  // also, calling .extend(-1000) will remove 1000ms, setting timer to 0ms if needed
  obj.extend = function(ms) {
    // break early if it already fired
    if (obj.called || obj.canceled) return false; 
    // clear old timer, calculate new timer
    clearTimeout(obj.timeout);
    obj.firetime += ms;
    var newDelay = obj.firetime - new Date(); // figure out new ms
    if (newDelay < 0) newDelay = 0;
    obj.timeout = setTimeout(callfunc, newDelay);
    return obj;
  };
  // Cancel the timer...  
  obj.cancel = function() {
    obj.canceled = true;
    clearTimeout(obj.timeout);
  };
  // call the initial timer...
  obj.timeout = setTimeout(callfunc, delay);
  // return our object with the helper functions....
  return obj;
}

var d = +new Date();
var timer = setAdvancedTimer(function() { alert('test'+ (+new Date() - d)); }, 1000);

timer.extend(1000);
// should alert about 2000ms later
gnarf
+1 Nice example.
jensgram
A: 

It may be not exactly what you want, but take a look anyway, maybe you can use it to your benefit.

There is a great solution written by my ex-coworker that can create special handler functions that can stop and start timeouts when required. It is most widely used when you need to create a small delay for hover events. Like when you want to hide a mouseover menu not exactly at the time when a mouse leaves it, but a few milliseconds later. But if a mouse comes back, you need to cancel the timeout.

The solution is a function called getDelayedHandlers. For example you have a function that shows and hides a menu

function handleMenu(show) {
    if (show) {
        // This part shows the menu
    } else {
        // This part hides the menu
    }
}

You can then create delayed handlers for it by doing so:

var handlers = handleMenu.getDelayedHandlers({
    in: 200, // 'in' timeout
    out: 300, // 'out' timeout
});

handlers becomes an object that contains two handler functions that when being called cancel the other one's timeout.

var element = $('menu_element');
element.observe('mouseover', handlers.over);
element.observe('mouseout', handlers.out);

P.S. For this solution to work you need to extend the Function object with the curry function, which is automatically done in Prototype.

Igor Zinov'yev
A: 

Another wrapper:

function SpecialTimeout(fn, ms) {
    this.ms = ms;
    this.fn = fn;
    this.timer = null;
    this.init();
}

SpecialTimeout.prototype.init = function() {
    this.cancel();
    this.timer = setTimeout(this.fn, this.ms);
    return this;
};

SpecialTimeout.prototype.change = function(ms) {
    this.ms += ms;
    this.init();
    return this;
};

SpecialTimeout.prototype.cancel = function() {
    if ( this.timer !== null ) {
        clearTimeout(this.timer);
        this.timer = null;
    }
    return this;
};

Usage:

var myTimer = new SpecialTimeout(function(){/*...*/}, 10000);

myTimer.change(-5000); // Retard by five seconds
myTimer.change(5000); // Extend by five seconds
myTimer.cancel(); // Cancel
myTimer.init(); // Restart

myTimer.change(1000).init(); // Chain!
J-P
Nice wrapper, (much cleaner than mine) but the 'ms' property doesn't take into account any time that may or may not of passed already... not sure if the OP requires this
gnarf
Nicely written code, but the "change" method needs to compensate correctly for the time that's already elapsed. I think this would be simple to add, via a call to Date.getTime. Instead of storing the delay till firing, the SpecialTimeout would store the absolute time at which it is supposed to fire.
nibot