28

I am using the setInterval() function to call a function every 20 seconds. However, one thing I noticed is that it the first time setInterval() actually calls the function is at 20 seconds (and not when setInterval() is called). Here is my current workaround:

dothis();
var i = setInterval(dothis, 20000);

Is there any way to achieve this without having this duplicate code?

Andrew
  • 227,796
  • 193
  • 515
  • 708

3 Answers3

47

Your method is THE normal way way of doing it.

If this comes up over and over, you could make a utility function that would execute the handler first and then set the interval:

function setIntervalAndExecute(fn, t) {
    fn();
    return(setInterval(fn, t));
}

Then, in your code, you could just do this:

var i = setIntervalAndExecute(dothis, 20000);
jfriend00
  • 683,504
  • 96
  • 985
  • 979
14

You can make an anonymous function and call it immediately, except you use the setTimeout function instead of setInterval.

(function doStuff() {
    //Do Stuff Here
    setTimeout(doStuff,20000);
})();

This will execute the function, then call it again in 20 seconds.

Note that depending on the behavior you desire or in some cases for performance, it can be better to use setTimeout over setInterval. The main difference is that setInterval calls that function whenever that timeout is up, REGARDLESS if the last call has finished executing or if an error occurs. Using setTimeout is this fashion ensures that the function finishes its execution before the timer resets. A lot of the tradeoffs are pretty apparent, but it is a good thing to keep in mind as you design your application.

EDIT In response to patrick dw's concern about having the need to cancel the timeout. The best solution is to not use the anonymous function, and just call it after declaration

var timeout;
function doStuff() {
    //doStuff 
    timeout = setTimeout(doStuff,20000);
}
doStuff()

Yes this is similar to what the OP was trying to avoid, but it does removes the need to call the function and then call the setInterval function, so you save a line of code. You can stop and start the function at anytime by:

//Stop it
clearTimeout(timeout);

//Start it
doStuff();
jyore
  • 4,715
  • 2
  • 21
  • 26
  • 1
    plus: `setTimeout` is preferable since it won't continue to repeat if there is an error. – Hemlock Sep 15 '11 at 00:35
  • +1 I think this is a good solution if there's no other need to invoke the function externally. – user113716 Sep 15 '11 at 00:36
  • ...although you're losing one bit of functionality with your current answer. OP had the return result of `setInterval` assigned to a variable, which may imply that the interval should be canceled at some point. To replicate the behavior, you'd need to make the recursive call via `setTimeout` contingent upon the state of a flag that can be set to halt the invocation. – user113716 Sep 15 '11 at 00:57
  • Of course, the trouble then would be restarting it once again. You'd probably need to place the whole thing in a function that can be invoked externally, or just expose the function by name in the enclosing variable environment so you can get to it to start it up again. – user113716 Sep 15 '11 at 01:00
  • Yeah, in case of the need to recall later and/or clear the timeout, then the best solution is probably found in my edit above – jyore Sep 15 '11 at 01:09
3

If your dothis function has no other need for a return value, you can have it return itself.

This will allow you to invoke and pass it at the same time. If the return value is otherwise ignored, it will be harmless.

function dothis() {
    // your code
    return dothis;
}

var i = setInterval(dothis(), 20000);

Otherwise, you could extend Function.prototype to give an invoke and return functionality to all your functions:

DEMO: http://jsfiddle.net/ZXeUz/

Function.prototype.invoke_assign = function() {
    var func = this,
        args = arguments;
    func.call.apply( func, arguments );
    return function() { func.call.apply( func, args ); };
};

setInterval( dothis.invoke_assign( 'thisArg', 1, 2, 3 ), 20000 );

// thisArg 1 2 3
// thisArg 1 2 3
// thisArg 1 2 3
// ...

This actually enhances things a bit. It lets you pass a set of arguments. The first argument will set the this value of the function you're invoking, and the rest of the arguments will be passed on as the regular arguments.

Because the function returned is wrapped in another function, you'll have identical behavior between the initial and interval invocations.

user113716
  • 318,772
  • 63
  • 451
  • 440