ゆう's Blog
タイマーに関する組み込み関数

setInterval()関数は、指定した間隔(ミリ秒)ごとに特定の関数を実行するタイマーを設定します。この関数は一意のIDを返し、このIDは後でclearInterval()関数に渡してタイマーを停止するために使用します。

// 1秒ごとにメッセージを表示するタイマーを設定
var intervalID = setInterval(function() {
    console.log("1秒経過しました");
}, 1000);

// 5秒後にタイマーを停止
setTimeout(function() {
    clearInterval(intervalID);
    console.log("タイマーを停止しました");
}, 5000);

このコードは、1秒ごとに"1秒経過しました"というメッセージを表示するタイマーを設定します。そして、5秒後にそのタイマーを停止します。その結果、"1秒経過しました"というメッセージは5回表示され、その後"タイマーを停止しました"と表示されます。

setTimeout()関数は、指定した時間(ミリ秒)が経過した後に特定の関数を一度だけ実行するタイマーを設定します。この関数も一意のIDを返し、このIDはclearTimeout()関数に渡してタイマーを停止するために使用します。

// 5秒後にメッセージを表示するタイマーを設定
var timeoutID = setTimeout(function() {
    console.log("5秒経過しました");
}, 5000);

// タイマーをすぐに停止
clearTimeout(timeoutID);

このコードは、5秒後に"5秒経過しました"というメッセージを表示するタイマーを設定します。しかし、その直後にclearTimeout()関数が呼び出されるため、メッセージは表示されません。


setTimeout関数の中で自身を再度呼び出すことで、setInterval関数と同様の動作を実現することができます。

ただし、setTimeoutを再帰的に使用すると、setIntervalとはいくつかの重要な違いがあります:

動的な遅延: setIntervalは一定の間隔で関数を実行しますが、再帰的なsetTimeoutでは各呼び出しで遅延時間を動的に変更することが可能です。
エラーハンドリング: setIntervalはエラーが発生しても次の呼び出しをスケジュールしますが、setTimeoutではエラーが発生すると次の呼び出しがスケジュールされません。
呼び出しのオーバーラップ防止: setIntervalは指定した間隔で関数を呼び出しますが、前の呼び出しが終了する前に次の呼び出しが開始される可能性があります。一方、再帰的なsetTimeoutでは前の呼び出しが終了してから次の呼び出しをスケジュールするため、この問題は発生しません。

以上のような理由から、特定の状況ではsetIntervalの代わりに再帰的なsetTimeoutを使用することが推奨されます。

let interval = 1000;
setTimeout(function main(){
・・・
・・・
・・・
interval = 1000 - Date.now() % 1000;
  setTimeout(main, interval);
}, interval);

このコードは、関数mainをほぼ正確に1秒ごとに実行するように設定されています。

ここでのポイントは、setTimeout(main, interval)が呼び出されるタイミングです。Date.now() % 1000は現在のミリ秒を1000で割った余りを返します。つまり、現在の秒の中で経過したミリ秒数を返します。これを1000から引くと、次の秒(つまり次の1000ミリ秒)までの残り時間が得られます。

したがって、interval = 1000 - Date.now() % 1000;は、次の秒までの残り時間を計算します。そして、その残り時間だけ待ってからmain関数を再度呼び出します。

この結果、main関数はほぼ正確に1秒ごと(つまり、時計の秒針が動くごと)に実行されます。

ただし、JavaScriptのタイマー関数は完全に正確ではないため、実際の間隔は1秒よりわずかに長くなる可能性があります。これは、JavaScriptがシングルスレッドで動作し、他のタスクが実行中の場合にはタイマーのコールバックが遅延するためです。


最初の一回をすぐに実行したい場合は、関数を一度手動で呼び出してからsetIntervalを設定する

function myFunction() {
  // ここに実行したいコードを書く
}

// 最初の一回をすぐに実行
myFunction();

// その後、指定した間隔で実行を続ける
setInterval(myFunction, 1000); // 1000ミリ秒(1秒)ごとにmyFunctionを実行


setIntervalの戻り値を一つ(最後に実行されたもの)しか保持していないため、setIntervalを連続使用すると、clearIntervalが効かなくなる。

setInterval前に必ずclearIntervalをコールし、前回作ったタイマーを止めるようにする。その後setIntervalで新たなタイマーを作る。setTimeoutの場合はその前にclearTimeoutする。