分が変わったときに関数を正確に実行するにはどうすればよいですか?setIntervalの使用は、分が変更されたときにトリガーすると機能する可能性があります。しかし、setIntervalが長時間実行されるプロセスのイベントループによって中断され、クロックとの同期が維持されない可能性があるのではないかと心配しています。
分が変わったときに関数を正確に実行するにはどうすればよいですか?
分が変わったときに関数を正確に実行するにはどうすればよいですか?setIntervalの使用は、分が変更されたときにトリガーすると機能する可能性があります。しかし、setIntervalが長時間実行されるプロセスのイベントループによって中断され、クロックとの同期が維持されない可能性があるのではないかと心配しています。
分が変わったときに関数を正確に実行するにはどうすればよいですか?
まず、タイマーの繰り返しに使用する必要があります。これは、定期的な実行を保証するためです。つまり、繰り返しの呼び出し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);
これはアイデアかもしれません。最大偏差は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ただし、javascriptのタイムアウトの精度については、この質問も参照してください。
Xミリ秒ごとにタイムアウトを設定し、1分が経過したかどうか、および関数が最後に呼び出されてからどのくらいの時間が経過したかを確認して、できるだけ正確にすることができますが、それだけです。
イベントループをブロックしているものがある可能性があるため、関数が1分後に正確にトリガーされることを100%確信することはできません。
それが重要な場合は、そのためにcronjobまたは別のNode.jsプロセスを使用することをお勧めします(イベントループがブロックされていないことを確認できます)。
資力:
http://www.sitepoint.com/creating-accurate-timers-in-javascript/
私はあなたのために可能な解決策を提示しました:
/* 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);
完璧ではありませんが、十分に信頼できるはずです。ユーザーがブラウザのタブを切り替えたり、コードで他のブロックタスクがページで実行されたりする可能性があるため、ブラウザソリューションが完全になることはありません。十分な信頼性があるかどうかは、ユーザー(および要件)が決定します。か否か。
ブラウザとnode.jsでテスト済み
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);
私の最初の考えは、Dateオブジェクトを使用して現在の時刻を取得することです。これにより、簡単な計算で分単位の設定間隔を設定できます。次に、5〜10分ごと、または適切と思われるものがあれば、それが降りるのが心配なので、新しい日付オブジェクトを使用して時刻を再確認し、それに応じて設定した間隔を再調整できます。
これは私の最初の考えですが、午前中にいくつかのコードを設定できます(ここでは午前2時のように)。
これはかなり簡単な解決策です...タイムアウトの間隔は、呼び出されるたびに調整されるため、ドリフトしないようになります。早期に起動した場合に備えて、50ミリ秒の安全性があります。
function onTheMinute(callback) {
const remaining = 60000 - (Date.now() % 60000);
setTimeout(() => {
callback.call(null);
onTheMinute(callback);
}, remaining + (remaining < 50 ? 60000 : 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)
}
小さな、おそらく興味深い事実:コールバック関数は、次のわずかな変更でのみ実行を開始します。
@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);
}