分钟变化时准确运行功能?

时间:2012-05-29 07:56:39

标签: javascript node.js

如果分钟发生变化,我怎样才能准确地运行功能?如果我在分钟更改时立即触发它,则使用setInterval可以工作。但是我担心setInterval会在长时间运行的过程中被事件循环中断而不能与时钟保持同步。

如何在分钟发生变化时准确运行功能?

9 个答案:

答案 0 :(得分:24)

首先,您应该使用setInterval来重复计时器,因为它(尝试)保证定期执行,即任何潜在的延迟都不会像重复的setTimeout调用那样叠加。这将每分钟执行一次你的功能:

var ONE_MINUTE = 60 * 1000;

function showTime() {
  console.log(new Date());
}

setInterval(showTime, ONE_MINUTE);

现在,我们需要做的是在恰当的时间开始:

function repeatEvery(func, interval) {
    // Check current time and calculate the delay until next interval
    var now = new Date(),
        delay = interval - now % interval;

    function start() {
        // Execute function now...
        func();
        // ... and every interval
        setInterval(func, interval);
    }

    // Delay execution until it's an even interval
    setTimeout(start, delay);
}

repeatEvery(showTime, ONE_MINUTE);

答案 1 :(得分:1)

这可能是一个想法。最大偏差应为1秒。如果您希望它更精确,请降低setTimeout 1 的毫秒数。

setTimeout(checkMinutes,1000);

function checkMinutes(){
  var now = new Date().getMinutes();
  if (now > checkMinutes.prevTime){
    // do something
    console.log('nextminute arrived');
  }
  checkMinutes.prevTime = now;
  setTimeout(checkChange,1000);
}

1 但是,另请参阅this question,关于javascript中超时的准确性

答案 2 :(得分:1)

您可以尝试尽可能准确,每隔X毫秒设置一次超时并检查分钟是否已经过去以及自上次调用该函数以来已经过了多少时间,但这就是它。

您无法100%确定您的功能会在1分钟后完全触发,因为可能会阻止事件循环。

如果它是至关重要的,我建议专门为此使用cronjob或单独的Node.js进程(这样你就可以确保事件循环没有被阻止)。

资源:

http://www.sitepoint.com/creating-accurate-timers-in-javascript/

答案 3 :(得分:1)

我为您提供了一个可能的解决方案:

/* Usage:
 *
 * coolerInterval( func, interval, triggerOnceEvery);
 *
 *   - func : the function to trigger
 *   - interval : interval that will adjust itself overtime checking the clock time
 *   - triggerOnceEvery : trigger your function once after X adjustments (default to 1)
 */
var coolerInterval = function(func, interval, triggerOnceEvery) {

    var startTime = new Date().getTime(),
        nextTick = startTime,
        count = 0;

    triggerOnceEvery = triggerOnceEvery || 1;

    var internalInterval = function() {

        nextTick += interval;
        count++;

        if(count == triggerOnceEvery) {

            func();
            count = 0;
        }

        setTimeout(internalInterval, nextTick - new Date().getTime());
    };

    internalInterval();

};

以下是每分钟打印一次时间戳的示例用法,但时间漂移每秒调整一次

coolerInterval(function() {

    console.log( new Date().getTime() );

}, 1000, 60);

它并不完美,但应该足够可靠。 考虑用户可以在浏览器上切换选项卡,或者您的代码可能在页面上运行一些其他阻止任务,因此浏览器解决方案永远不会是完美的,由您(以及您的要求)来决定它是否足够可靠或不。

答案 4 :(得分:0)

我的第一个想法是使用 Date object 来获取当前时间。这将允许您使用一些简单的数学设置分钟上的设置间隔。然后,由于你担心它会下降,每5-10分钟或者你认为合适的任何东西,你可以使用新的日期对象重新检查时间并相应地重新调整你的设定间隔。

这只是我的第一个想法,虽然早上我可以提出一些代码(就像凌晨2点)。

答案 5 :(得分:0)

在浏览器和node.js中测试

  • 在分钟变化之前睡到2秒然后等待变化
  • 您可以删除记录,因为它在日志中变得非常混乱

        function onMinute(cb,init) {
            
            if (typeof cb === 'function') {
            
                var start_time=new Date(),timeslice = start_time.toString(),timeslices = timeslice.split(":"),start_minute=timeslices[1],last_minute=start_minute;
                var seconds = 60 - Number(timeslices[2].substr(0,2));
                var timer_id;
                var spin = function (){
                                    console.log("awake:ready..set..");
                                    var spin_id = setInterval (function () {
                                        
                                        var time=new Date(),timeslice = time.toString(),timeslices = timeslice.split(":"),minute=timeslices[1];
                                        if (last_minute!==minute) {
                                            console.log("go!");
                                            clearInterval(spin_id);
                                            last_minute=minute;
                                            cb(timeslice.split(" ")[4],Number(minute),time,timeslice);   
                                            console.log("snoozing..");
                                            setTimeout(spin,58000);
                                        }
                                
                                    },100);
                                    
                                };
                
                setTimeout(spin,(seconds-2)*1000);
                
                if (init) {
                    cb(timeslice.split(" ")[4],Number(start_minute),start_time,timeslice,seconds);   
                }
            }
        
        }
        
        
        onMinute(function (timestr,minute,time,timetext,seconds) {
            if (seconds!==undefined) {
                console.log("started waiting for minute changes at",timestr,seconds,"seconds till first epoch");
            } else {
                console.log("it's",timestr,"and all is well");
            }
        },true);

答案 6 :(得分:0)

这是一个非常简单的解决方案...超时间隔在每次被调用时都会进行调整,这样它就不会漂移,并且有50ms的安全性,以防它提前触发。

function onTheMinute(callback) {
    const remaining = 60000 - (Date.now() % 60000);
    setTimeout(() => {
        callback.call(null);
        onTheMinute(callback);
    }, remaining + (remaining < 50 ? 60000 : 0));
}

答案 7 :(得分:0)

这是基于@Linus的帖子和@Brad的评论的另一种解决方案。唯一的区别是它不能通过递归调用父函数来工作,而只是setInterval()setTimeout()的组合:

function callEveryInterval(callback, callInterval){
    // Initiate the callback function to be called every
    // *callInterval* milliseconds.
    setInterval(interval => {
        // We don't know when exactly the program is going to starts 
        // running, initialize the setInterval() function and, from 
        // thereon, keep calling the callback function. So there's almost
        // surely going to be an offset between the host's system 
        // clock's minute change and the setInterval()'s ticks.
        // The *delay* variable defines the necessary delay for the
        // actual callback via setTimeout().
        let delay = interval - new Date()%interval
        setTimeout(() => callback(), delay)
    }, callInterval, callInterval)
}

可能是一个有趣的小事实:回调函数仅在下一个下一分钟的更改开始执行。

答案 8 :(得分:0)

@Linus与setInterval提出的解决方案通常是正确的,但只有在两分钟到正好60秒之间才能起作用。这个看似显而易见的假设会在存在leap second的情况下失效,或者如果代码在笔记本电脑上运行并暂停了几秒钟,则可能更常见。

如果您需要处理此类情况,则最好在每次间隔时手动调用setTimeout进行调整。像下面这样的事情应该可以完成工作:

function repeatEvery( func, interval ) {
    function repeater() {
        repeatEvery( func, interval);
        func();
    }
    var now = new Date();
    var delay = interval - now % interval;

    setTimeout(repeater, delay);
}