概要
リアルタイムにユーザー入力を処理するとは、「すべてを即時に処理すること」ではない。
それは**“必要なタイミングだけを抽出し、過剰な処理を制御する”ための戦略的設計**である。
JavaScriptでは、スクロール・リサイズ・キー入力・マウス操作など高頻度のイベントが大量に発生する。
それらを無制限に処理することは、UI崩壊・パフォーマンス劣化・クラッシュにつながる。
本稿では、**デバウンス(debounce)とスロットリング(throttle)**を明確に定義し、
どのように設計的に使い分けるかを解説する。
1. デバウンス(Debounce)とは
function debounce(fn, delay) {
let timeout;
return (...args) => {
clearTimeout(timeout);
timeout = setTimeout(() => fn(...args), delay);
};
}
- ✅ 一定時間「入力が止まった後」に実行
- ✅ 主に 検索フォーム・オートセーブ・リアルタイムバリデーション などに使用
2. スロットリング(Throttle)とは
function throttle(fn, interval) {
let last = 0;
return (...args) => {
const now = Date.now();
if (now - last >= interval) {
last = now;
fn(...args);
}
};
}
- ✅ 一定間隔で強制的に実行を制限
- ✅ 主に スクロール・リサイズ・マウス移動処理 などに使用
3. 違いの比較
特性 | Debounce | Throttle |
---|---|---|
実行タイミング | 入力が止まってから | 一定間隔で強制実行 |
ユースケース | フォーム送信、検索、バリデーション | スクロール、リサイズ、カーソル追従 |
発火回数 | 最後の1回 | 定期的に複数回 |
4. 高速環境での制御とアニメーション挙動
// rAFと組み合わせて負荷軽減しつつ滑らかに描画
let ticking = false;
window.addEventListener("scroll", () => {
if (!ticking) {
window.requestAnimationFrame(() => {
handleScroll();
ticking = false;
});
ticking = true;
}
});
- ✅
requestAnimationFrame
を使うことでFPSと描画タイミングを同期 - ✅ イベント処理と描画負荷を切り離せる
5. ユースケース別設計指針
ユースケース | 推奨制御方法 |
---|---|
検索ボックスの入力 | Debounce(300ms前後) |
ウィンドウサイズ変更 | Throttle(200ms前後) |
リアルタイム変換 | Debounce + 最終送信保証 |
スクロール中のDOM制御 | Throttle + requestAnimationFrame |
文字数カウント / フォーム入力 | Debounce(100〜300ms) |
設計判断フロー
① ユーザー操作が「停止した後に」実行すべきか? → Debounce
② 一定間隔で「定期的に」処理したいか? → Throttle
③ リアルタイム性が必要でも「頻度が高すぎる」か? → rAF + 制御構造
④ イベントを100%検知したいか? → Debounceのtrailingフラグを有効化
⑤ ユーザー体験を阻害しない処理タイミングか? → イベント発火点と描画負荷を分離
よくあるミスと対策
❌ 入力のたびにAPIリクエストを飛ばす(100msで10回以上)
→ ✅ Debounceで遅延制御し、1回に抑える
❌ スクロール中に毎回処理が走ってFPSが激落ち
→ ✅ Throttle + rAFで滑らかに制御
❌ Throttleのインターバルが短すぎて意味がない
→ ✅ 200ms〜500msに設計値を上げることで処理頻度を現実的に
結語
デバウンスとスロットリングは「処理を減らす手段」ではない。
それは**“ユーザー行動の粒度に応じて、実行タイミングと頻度を意図的に設計するための制御構造”**である。
- Debounce は「静止後に実行」
- Throttle は「頻度を制限して定期実行」
- 描画系は rAFで描画サイクルと同期させる
JavaScriptにおけるリアルタイム制御とは、
“入力を解釈し、実行の意図とタイミングを分離することでUXと性能を両立させるための設計戦略”である。