Joshua Soileau
Joshua Soileau

Reputation: 3015

Javascript Timing: How do I execute a function every 5, 7, and 8 seconds?

Let's say I have a function log()

var log = function(i) {
    console.log('Executing at time: ' + i);
}

And I want to execute log() on an interval, every x, y, and z seconds?

Let's arbitrarily set x, y, and z to 5, 7, and 8.

var x = 5;
var y = 7;
var z = 8;

And every x, y, and z seconds I want to call log():

// Every 5 seconds:
log(x);
// Every 7 seconds:
log(y);
// Every 8 seconds:
log(z);

I could use try using three setTimeout()s to run these functions alone. But wait, javascript isn't asynchronous, so the execution of one setTimeout() will prevent the execution the other two... Hmm...

I could calculate the difference in the times, so that the delay caused by the first setTimeout() won't break the timing of the second and third:

// x is at 5 seconds, run 5000 milliseconds after the start
setTimeout(function() {  log(x); }, 5000);
// y is at 7 seconds after start, or 2000 milliseconds after x is done
setTimeout(function() {  log(y); }, 2000);
// z is at 7 seconds after start, or 1000 milliseconds after y is done
setTimeout(function() {  log(z); }, 1000);

But the timing would never be exactly right, as the execution of my log() function will take at least a small amount of time. Also, this get's messy when I try and add more log() commands in, or when I try and repeat the entire process at a set interval.

I could use a loop, count the seconds, and on the x, y, and zth second, execute my commands:

var i = 0;
while(i<10) {
    // wait 1 second (pointless function, don't know a better way to wait a second)
    setTimeout(function() { console.log(' ... '); }, 1000);
    i++;

    // execute x, y, or z depending on what time it is
    switch(i) {
        case 4: 
            log(x);
        case 6: 
            log(y);
        case 7: 
            log(z);
    }
}

But this method of 'counting', where I execute a useless command using setTimeout(), really just bothers me as inefficient.

Is there a better way to measure time in javascript, and have a function execute at certain times in the future? The key for me is that it's not just "run log() every 5 seconds", because I understand that. It's "run log() at times x, y and z" that confuses me.

Upvotes: 3

Views: 4961

Answers (5)

Ehtesham
Ehtesham

Reputation: 2985

hmmm a naive solution could be like this. With jQuery though if you have only these three intervals a nice solution would be to use deferrds.

function log (val) {
    console.log(val);
}

var intervals = [{interval: 5000, logVal: 'x', logged: false},
                 {interval: 7000, logVal: 'y', logged: false},
                 {interval: 8000, logVal: 'z', logged: false}];

var startTime;

function myLog () {
    var endTime = new Date().getTime();
    var diff = endTime - startTime;

    for (var i = 0; i < (intervals.length - 1); i++) {
        if (diff >= intervals[i].interval && diff < intervals[i+1].interval && !intervals[i].logged) {
            log(intervals[i].logVal);
            intervals[i].logged = true;
        }
    }

    // for last interval
    if (diff >= intervals[i].interval) {
        log(intervals[i].logVal);
        startTime = new Date().getTime();
        return;
    }

    // Reset Time and run again
    setTimeout(myLog, 500);
}

startTime = new Date().getTime();
setTimeout(myLog, 1000);

here's the fiddle http://jsfiddle.net/jD8zn/4/

Upvotes: 0

ZER0
ZER0

Reputation: 25322

You need to set the next timeout inside the previous one:

// x is at 5 seconds, run 5000 milliseconds after the start
setTimeout(function() {
  log(x);
  setTimeout(function() {
    log(y);
    setTimeout(function() {
      log(z);
    }, 1000);
  }, 2000);

}, 5000);

In this way you don't schedule up front all the timeouts, but just one at the time, and there is less error's margin (the second timeout will start after the first is ending, and so on.

Edit I missed actually that the OP wanted this repeated. In this case, you just need to have the first function expression with a name instead of an anonymous one, and call it at the end:

setTimeout(function entry() {
   log(x);
   setTimeout(function() {
     log(y);
     setTimeout(function() {
       log(z);
       setTimeout(entry, 5000);
     }, 1000);
   }, 2000);
 }, 5000);

My comment was about to avoid to nest, so you could have a promise-like approach. Notice that it's also possible doing that with an additional function as jm0 made, however is an additional pollution of the scope, where using function expression avoid that.

Upvotes: 0

jm0
jm0

Reputation: 3444

Borrowing from ZER0's idea, I would set up a recursive timeout chain similar to this --

function startTimeoutChain() { //Represents 1 loop cyele

setTimeout(function() {
  log(x);
  setTimeout(function() {
    log(y);
    setTimeout(function() {
      log(z);
      startTimeoutChain(); // Begin next loop
    }, 1000);
  }, 2000);
}, 5000);

}

Just call that once & you should have something that is pretty robust. Even if the clock drifts, these values will be calibrated to each other because they are done with the delta of the times, essentially.

Though to be honest, I'm having a hard time understanding if you want to call these on the 5th, 7th, 8th seconds of some loop cycle or simply every 5, 7, 8 seconds independently of some master loop.

Upvotes: 0

geonunez
geonunez

Reputation: 1309

You can use the function "setInterval". Here is a example:

var interval1Id = setInterval(function(){
    console.log("logging every 5 seconds");
},5000);

var interval2Id = setInterval(function(){
    console.log("logging every 7 seconds");
},7000);

var interval3Id = setInterval(function(){
   console.log("logging every 8 seconds");
},8000);

Variables "intervalXId" are saved just in case you want to stop any interval.

Upvotes: 2

Mark
Mark

Reputation: 2822

The easy solution is to set a timer function that's called every second and keeps a count of how many seconds have elapsed; if the count is a multiple of 5, 7, or 8, the timer function calls the function you want to execute.

Upvotes: 0

Related Questions