Javascript计时:如何每5、7和8秒执行一个函数

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

本文关键字:执行 函数 一个 8秒 计时 何每 Javascript      更新时间:2024-04-29

假设我有一个函数log()

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

我想每隔x、y和z秒执行一次log()

让我们任意设置x、y和z为5、7和8。

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

每x、y和z秒,我都想调用log():

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

我可以尝试使用三个setTimeout()单独运行这些函数。但是等等,javascript不是异步的,所以一个setTimeout()的执行将阻止其他两个的执行。。。嗯…

我可以计算时间的差异,这样第一个setTimeout()引起的延迟就不会破坏第二个和第三个的定时:

// 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);

但时机永远不会完全正确,因为我的log()函数的执行至少需要一小段时间。此外,当我尝试在中添加更多log()命令时,或者当我尝试以设定的间隔重复整个过程时,这会很混乱。

我可以使用循环,计算秒数,在x、y和z秒执行我的命令:

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);
    }
}

但这种"计数"方法,即我使用setTimeout()执行一个无用的命令,真的让我觉得效率低下。

有没有更好的方法来测量javascript中的时间,并让函数在未来的特定时间执行?对我来说,关键是它不仅仅是"每5秒运行一次log()",因为我理解这一点。让我困惑的是"在x、y和z时间运行log()"

您可以使用函数"setInterval"。这里有一个例子:

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);

变量"intervalXId"被保存,以防您想要停止任何间隔。

简单的解决方案是设置一个计时器函数,该函数每秒调用一次,并记录经过的秒数;如果计数是5、7或8的倍数,则计时器函数将调用要执行的函数。

借用ZER0的想法,我将建立一个类似于此的递归超时链——

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);
}

只需调用一次&你应该有一些非常健壮的东西。即使时钟漂移,这些值也会相互校准,因为它们本质上是用时间的增量来完成的。

尽管说实话,我很难理解你是想在某个循环的第5、7、8秒调用这些,还是简单地每5、7和8秒调用一次,独立于某个主循环。

您需要在上一个超时内设置下一个超时:

// 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);

这样,你就不会提前安排所有的超时,而是一次只安排一次,而且错误幅度较小(第二次超时将在第一次超时结束后开始,以此类推

编辑事实上,我错过了OP希望重复这一点。在这种情况下,您只需要有第一个带有名称而不是匿名名称的函数表达式,并在最后调用它:

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

我的评论是要避免嵌套,所以你可以采取类似承诺的方法。请注意,使用jm0这样的附加函数也可以实现这一点,但这会对范围造成额外的污染,使用函数表达式可以避免这种情况。

hmmm一个简单的解决方案可能是这样的。不过,对于jQuery,如果只有这三个间隔,那么一个很好的解决方案就是使用延迟。

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);

这是小提琴http://jsfiddle.net/jD8zn/4/