この記事はtraP Advent Calendar 2023 14日目の記事です。
はじめに
こんにちは。19Bの@kegraという者です。いつもゲーム開発とかWeb開発あたりをやってます。
今日はJavaScript(TypeScript)で最近やらかした割と初歩的なデカいミスを紹介したいと思います。
setInterval()による定期ポーリング
僕は今あるシステムを作っており、そのシステムではクライアントサイドが定期的にサーバーのAPIを叩いて情報を取ってくる感じになっています。いわゆるポーリングです。
クライアントサイドはWebフロントエンドで作っており、僕はこの実装でsetInterval()
を使っていました。
setInterval(() => {
updateData();
}, 5000); // 5秒おきにポーリング
そこで本当にしょうもないのですが、第2引数を書き忘れていました。
setInterval(() => {
updateData();
}); // 書き忘れ
すると何が起こるか
伝家の宝刀、MDNを見てみましょう。[1]
https://developer.mozilla.org/ja/docs/Web/API/setInterval
delay (省略可)
指定した関数またはコードを実行する前にタイマーが待つべき時間をミリ秒 (1/1000 秒) 単位で指定します。引数が 10 より小さい場合は、10 を使用します。実際の遅延が長くなることがあります。例えば遅延の制約をご覧ください。
一応setTimeoutの方も
https://developer.mozilla.org/ja/docs/Web/API/setTimeout
delay (省略可)
指定した関数やコードを実行する前に待つタイマーの時間をミリ秒 (1/1000 秒) 単位で指定します。この引数を省略すると値 0 を使用しますので "直ちに" 実行する、より正確に言えばできるだけ早く実行することを意味します。
なお、どちらの場合も、実際の遅延が想定より長くなることがあります。後述する遅延が指定値より長い理由をご覧ください。
また、値が数値でない場合、暗黙のうちに型強制が行われ、数値に変換されることにも注意してください。これは予期しない、驚くべき結果につながる可能性があります。例として、delay の値が数値でない場合は暗黙に数値に強制されるを参照してください。
あんまり明言されていないのですが、省略時の挙動としては10ms間隔になるか、あるいは「出来るだけ早く実行」となるものと考えられます。
従って、数ms間隔でサーバを高速連打する最悪のフロントエンドが爆誕します。
そしてサーバは爆発しました。
自分はこれを夜にデプロイしたあと気付かず放置しており、翌朝SysAd班の皆さんが気付いて対応に追われてました。マジでごめんなさい。
教訓
setInterval
の第2引数には気を付けよう!
というか開発にはTypeScriptを使ってたのになんでエラー出なかったんだと思いますが、そもそもAPI規格上省略が許されているんですね。エラーを出してくれない訳です。
万全を期すなら、timeout
の省略を許していない以下のような関数を定義してそれを使うのが良いのかもしれません。
function safeSetInterval(handler: TimerHandler, timeout: number, ...arguments: any[]) {
return setInterval(handler, timeout, ...arguments);
}
明日は@comaviusくんの担当です。お楽しみに!
setInterval/setTimeoutはECMAScriptの仕様上には存在せず、HTML Living Standardで規定されている ↩︎