「ブラウザ上で定期的に〇〇を実行する」処理をTypeScriptで実装してみる
フロントエンド開発において「ブラウザ上で定期的に〇〇を実行する」処理を実装したい時って、たまにあると思います。
単純な処理であれば、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