概要
高頻度で発火するイベント(スクロール、リサイズ、キー入力など)は、
そのまま処理するとパフォーマンスのボトルネックになり得る。
このような場面で重要なのが、イベントの発火頻度を意図的に制御する技術:debounce / throttle / requestAnimationFrame である。
本稿では、それぞれの構文と設計意図を明確にし、
**「いつ使うべきか」「何を制御すべきか」「どう設計すべきか」**にフォーカスして解説する。
1. debounce(デバウンス)
意図:最後の1回だけ処理したい
function debounce(fn, delay) {
let timer;
return function (...args) {
clearTimeout(timer);
timer = setTimeout(() => fn.apply(this, args), delay);
};
}
const onInput = debounce((e) => {
console.log('Searching:', e.target.value);
}, 500);
- ✅ 入力フィールドの変化 → 最後の入力だけを処理したい時に有効
- ✅ ネットワークや再描画コストの高い処理を不要に走らせない
2. throttle(スロットル)
意図:一定間隔ごとに処理したい
function throttle(fn, interval) {
let lastTime = 0;
return function (...args) {
const now = Date.now();
if (now - lastTime >= interval) {
lastTime = now;
fn.apply(this, args);
}
};
}
const onScroll = throttle(() => {
console.log('scrolling...');
}, 100);
- ✅ スクロールやリサイズ → 一定間隔でしか処理したくない
- ✅ CPU負荷の軽減・描画フレームの安定化に寄与
3. requestAnimationFrame(rAF)
意図:次の描画タイミングで最適に実行したい
function onScroll() {
requestAnimationFrame(() => {
updateScrollPosition();
});
}
- ✅ アニメーション、DOM描画のフレーム最適化
- ✅ ブラウザのレンダリングタイミングに合わせて実行されるためスムーズなUIを実現
4. 選定マトリクス
イベント種別 | 処理の性質 | 推奨制御法 |
---|---|---|
入力欄の検索 | 最終値のみ使いたい | debounce |
スクロール監視 | 一定間隔で実行したい | throttle |
ウィンドウリサイズ | 軽量処理だが高頻度 |
throttle または rAF
|
カーソル追従 | 毎フレーム更新が理想 | requestAnimationFrame |
設計判断フロー
① イベントがどの程度の頻度で発火するか? → msオーダーなら制御必須
② 処理は「最後の1回」だけで良いか? → debounce
③ 処理は「一定間隔ごと」に行うのが理想か? → throttle
④ 描画パフォーマンスへの影響はあるか? → requestAnimationFrame
⑤ 重い処理か? → debounceでバースト回避 or throttleで制限
よくあるミスと対策
❌ スクロールイベントに直接処理をバインドしてカクつき発生
→ ✅ throttleまたはrAFで制限をかける
❌ debounceでフォームバリデーションして、ユーザー体感が遅延
→ ✅ 即時バリデーション or throttleに変更する戦略を検討
❌ throttleとrAFを混用し、更新がダブって逆効果
→ ✅ 一つの制御構文に統一し、挙動とフレーム同期を整理する
結語
タイミング制御は「パフォーマンスのため」だけではない。
それは**“処理の意図・タイミング・責務を設計として明示することで、ユーザー体験と開発効率を両立させる戦略”**である。
- debounce は「最後の一回」
- throttle は「一定間隔」
- requestAnimationFrame は「最適な描画タイミング」
JavaScriptにおけるタイミング制御とは、
“イベント頻度と処理責任のバランスを設計として表現し、性能と体験を同時に最適化する技術戦略である。”