22

分が変わったときに関数を正確に実行するにはどうすればよいですか?setIntervalの使用は、分が変更されたときにトリガーすると機能する可能性があります。しかし、setIntervalが長時間実行されるプロセスのイベントループによって中断され、クロックとの同期が維持されない可能性があるのではないかと心配しています。

分が変わったときに関数を正確に実行するにはどうすればよいですか?

4

9 に答える 9

32

まず、タイマーの繰り返しに使用する必要があります。これは、定期的な実行を保証するためです。つまり、繰り返しの呼び出しsetIntervalの場合のように、潜在的な遅延が積み重なることはありません。setTimeoutこれにより、関数が1分ごとに実行されます。

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);
于 2012-05-29T10:17:21.953 に答える
2

これはアイデアかもしれません。最大偏差は1秒である必要があります。より正確にしたい場合は、ミリ秒をsetTimeout1に下げてください。

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ただし、javascriptのタイムアウトの精度については、この質問も参照してください。

于 2012-05-29T08:13:07.880 に答える
1

Xミリ秒ごとにタイムアウトを設定し、1分が経過したかどうか、および関数が最後に呼び出されてからどのくらいの時間が経過したかを確認して、できるだけ正確にすることができますが、それだけです。

イベントループをブロックしているものがある可能性があるため、関数が1分後に正確にトリガーされることを100%確信することはできません。

それが重要な場合は、そのためにcronjobまたは別のNode.jsプロセスを使用することをお勧めします(イベントループがブロックされていないことを確認できます)。

資力:

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

于 2012-05-29T08:13:24.230 に答える
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();

};

以下は、タイムスタンプを1分ごとに出力する使用例ですが、タイムドリフトは1秒ごとに調整されます

coolerInterval(function() {

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

}, 1000, 60);

完璧ではありませんが、十分に信頼できるはずです。ユーザーがブラウザのタブを切り替えたり、コードで他のブロックタスクがページで実行されたりする可能性があるため、ブラウザソリューションが完全になることはありません。十分な信頼性があるかどうかは、ユーザー(および要件)が決定します。か否か。

于 2012-05-29T08:38:14.290 に答える
1

ブラウザと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);

于 2018-04-04T02:59:36.830 に答える
0

私の最初の考えは、Dateオブジェクトを使用して現在の時刻を取得することです。これにより、簡単な計算で分単位の設定間隔を設定できます。次に、5〜10分ごと、または適切と思われるものがあれば、それが降りるのが心配なので、新しい日付オブジェクトを使用して時刻を再確認し、それに応じて設定した間隔を再調整できます。

これは私の最初の考えですが、午前中にいくつかのコードを設定できます(ここでは午前2時のように)。

于 2012-05-29T08:07:38.350 に答える
0

これはかなり簡単な解決策です...タイムアウトの間隔は、呼び出されるたびに調整されるため、ドリフトしないようになります。早期に起動した場合に備えて、50ミリ秒の安全性があります。

function onTheMinute(callback) {
    const remaining = 60000 - (Date.now() % 60000);
    setTimeout(() => {
        callback.call(null);
        onTheMinute(callback);
    }, remaining + (remaining < 50 ? 60000 : 0));
}
于 2018-12-17T12:21:09.137 に答える
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)
}

小さな、おそらく興味深い事実:コールバック関数は、次のわずかな変更でのみ実行を開始します。

于 2019-02-08T20:05:24.977 に答える
0

@Linusによって提案された解決策setIntervalは一般的に正しいですが、2分の間で正確に60秒ある場合にのみ機能します。この一見明らかな仮定は、うるう秒が存在する場合、またはおそらくより頻繁に、コードが数秒間中断されたラップトップで実行された場合に機能しなくなります。

このような場合に対処する必要がある場合はsetTimeout、間隔ごとに手動で調整を呼び出すのが最善です。次のようなものがその仕事をするはずです:

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

    setTimeout(repeater, delay);
}
于 2019-02-23T19:25:27.740 に答える