「ブラウザ上で定期的に〇〇を実行する」処理をTypeScriptで実装してみる

2022/08/11に公開

フロントエンド開発において「ブラウザ上で定期的に〇〇を実行する」処理を実装したい時って、たまにあると思います。
単純な処理であれば、setIntervalで定期的に関数を実行したり、setTimeoutで再帰的に関数を呼び出したりすれば、事足ります。

しかしながら、まれに「定期的に〇〇を実行するが、処理Aが実行されている間は〇〇を一時的に止めて、処理Aが完了後に再開したい。」みたいな条件が追加される時もあります。
例えば「非同期処理が走っている間は一時的に止めて、非同期処理完了後に再開したい」とか…

こういった条件が入ると、定期的処理のコードって妙に煩雑になってしまうんですよね。
…ということで、汎用性を持たせつつ、カンタンに定期的処理を実現できるコードをTypeScriptで書いてみました。

サンプルコード

const setRoutine = ({
  callback // 定期的に実行する関数
  interval = 5 * 1000, // callbackの実行間隔(ミリ秒)
}: {
  callback: Function
  interval: number
}) => {
  // タイマーID
  let timerId = -1
  // 再帰関数
  const repeater = () => {
    callback()
    timerId = window.setTimeout(repeater, interval)
  }

  return {
    // 定期的処理を開始
    start: () => {
      if (timerId !== -1) return
      timerId = window.setTimeout(repeater, interval)
    },
    // 定期的処理を停止
    stop: () => {
      if (timerId === -1) return
      clearTimeout(timerId)
      timerId = -1
    },
    // 定期的処理が実行中か
    getIsWorking: () => timerId !== -1
  }
}

// 使い方
const routine = setRoutine({
  callback: () => { console.log('hoge') } // 定期的に実行する関数
  interval: 3 * 1000, // 3秒間隔で実行
})

// 定期的処理を開始
routine.start()

// 10秒後に、定期的処理を停止
window.setTimeout(() => {
  routine.stop()
}, 10 * 1000)

// 20秒後に、定期的処理を再開
window.setTimeout(() => {
  routine.start()
}, 20 * 1000)

サンプルコードの解説

コードの内容はとても単純です。
クロージャを利用して、タイマーID(timerId)と、再帰関数(repeater)を関数スコープ内に閉じ込めて、外側から制御しているだけです。

使い方の解説ですが、まずintervalに定期的処理の実行間隔(ミリ秒)、callbackに定期的に実行する関数をいれて、setRoutineに渡します。
すると、setRoutineはオブジェクト(上記コードのroutine)を返却します。
あとは、routine.start()routine.stop()を使って、任意のタイミングで定期的処理を開始・停止するだけです。
また、routine.getIsWorking()で「現在、定期的処理が実行中か」を取得できます。

非同期処理と組み合わせた例がコチラ

const routine = setRoutine({
  interval: 3 * 1000,
  callback: () => { console.log('hoge') }
})

// 非同期処理の直前で、定期的処理を一時停止
routine.stop()

// 何らかの非同期処理
Promise.all([…])
  .then((res) => {…})
  .catch((err) => {…})
  .finaly(() => {
   // 非同期処理が完了したタイミングで、定期的処理を再開
   routine.start() 
  })

余談

今回はコードの見やすさからクロージャを利用しましたが、Classを使っても同じ機能を実現できると思います。
ここらへんは好みかなと。

Discussion