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

旧城冷巷雨未停 提交于 2019-12-12 13:25:30

问题


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.


回答1:


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.




回答2:


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.




回答3:


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.




回答4:


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.




回答5:


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/



来源:https://stackoverflow.com/questions/20460190/javascript-timing-how-do-i-execute-a-function-every-5-7-and-8-seconds

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!