【JS応用|豆知識】setIntervalの落とし穴を回避する!安全でモダンな非同期処理の書き方

導入: なぜsetIntervalの理解が重要なのか

Web開発において、定期的に処理を実行したい場面は非常に多くあります。例えば、カウントダウンタイマーや、リアルタイムで更新される株価表示などです。しかし、JavaScriptのsetIntervalを安易に使うと、ブラウザの負荷増大やメモリリークを引き起こす可能性があります。この記事では、setIntervalの基礎から、現場で推奨されるより安全な代替案までを解説します。

基礎知識: setIntervalとJavaScriptの非同期処理

setIntervalは、指定したミリ秒間隔で繰り返し処理を実行する関数です。仕組みとしては、ブラウザの「タイマーイベント」に処理を登録する形になります。
注意すべき点は、JavaScriptが「シングルスレッド」で動作していることです。もし間隔内に処理が完了しなかった場合、処理が積み重なってしまい、画面のフリーズや意図しない動作を招くことがあります。これが「非同期処理」におけるsetIntervalの最大の課題です。

実装/解決策: setTimeoutによる再帰的な呼び出し

現場で推奨される手法は、setIntervalを使わずにsetTimeoutを再帰的に呼び出す方法です。これには以下のメリットがあります。
1. 前回の処理が終わってから次のタイマーをセットするため、処理が重なってもフリーズしない。
2. 状況に応じて(処理結果によって)実行間隔を動的に変更できる。
3. 処理を停止させたいタイミングを細かく制御できる。

サンプルプログラム: 安全なタイマー実装

以下は、非同期処理を考慮した安全なタイマーの実装例です。

// 実行するかどうかを管理するフラグ
let isRunning = true;

// 繰り返し処理の関数
const tick = async () => {
  if (!isRunning) return;

  try {
    // ここでAPI呼び出しなどの非同期処理を実行する想定
    console.log("処理を実行中...");
    
    // 処理が重い場合を想定した擬似的な待機
    await new Promise(resolve => setTimeout(resolve, 1000));
    
  } catch (error) {
    console.error("エラーが発生しました", error);
  } finally {
    // 処理が終わった後に、次のタイマーを予約する(再帰)
    // これにより、処理が重なっても前の完了を待ってから次が走る
    if (isRunning) {
      setTimeout(tick, 2000); // 2秒の間隔を空けて再実行
    }
  }
};

// タイマー開始
tick();

// タイマーを止める場合はisRunningをfalseにする
// isRunning = false;

応用・注意点: 現場で役立つアドバイス

現場で最も注意すべきは「クリーンアップ処理」です。Reactなどのフレームワークを使用している場合、コンポーネントがアンマウントされた後もタイマーが動き続けると、存在しないDOMを操作しようとしてエラーが発生します。
必ずタイマーのIDを管理するか、上記のようにフラグを使って「コンポーネントが不要になったら処理を中断する」というロジックを組み込むようにしましょう。また、ブラウザがバックグラウンドに回るとタイマーの精度が落ちる(スロットリングされる)仕様があることも、設計時には忘れないようにしてください。

タイトルとURLをコピーしました