最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

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

programmeradmin1浏览0评论

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() mands 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 mands:

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

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() mands 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 mands:

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

Share Improve this question asked Dec 8, 2013 at 22:46 Joshua SoileauJoshua Soileau 3,0259 gold badges44 silver badges52 bronze badges 10
  • Why not just setInterval(func1, 5000); setInterval(func2, 7000); setInterval(func3, 8000);? – Waleed Khan Commented Dec 8, 2013 at 22:48
  • There's no way to make the timing exactly perfect with JS. setInterval and setTimeout are only guaranteed to try to run the function after the interval has elapsed. They don't enforce that exactly the interval has elapsed. – zzzzBov Commented Dec 8, 2013 at 22:49
  • Since you're concerned about the time delay introduced by the routine itself, perhaps a web worker would help here? I.e., each 5, 7, and 8 seconds, you pass the routine out to a worker which will not prevent execution of the other calls. – Marc Commented Dec 8, 2013 at 22:49
  • 1 setTimeout is asynchronous (won't wait for the function to execute), so you do not need to pute those differences at all – Bergi Commented Dec 8, 2013 at 22:50
  • 1 "But wait, javascript isn't asynchronous [...]". JavaScript is (can be) asynchronous, but it is not concurrent. – Felix Kling Commented Dec 8, 2013 at 22:55
 |  Show 5 more ments

5 Answers 5

Reset to default 2

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.

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.

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.

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

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/jD8zn/4/

与本文相关的文章

发布评论

评论列表(0)

  1. 暂无评论