debounceとthrottle

特にフロントエンドの開発において、処理が終わるのを待ってから、または連続で実行されるイベントなどで呼び出しの間隔を制御したいことは多々あります。

そんなときに使えるTipsです。

debounce

debounceは、連続で呼び出しがある場合に、最後に呼び出されたと判定されたら、特定の処理を呼び出すテクニックです。
例えば、自動バックアップのあるメモ帳アプリで、ユーザ入力の度にバックエンドにリクエストを送って保存したいとします。その場合に、keydownイベントを拾って、keydownイベントが発火してから指定の時間が経過したらバックエンドに現在の入力情報を送るといった事ができます。

コード

setTimeout関数の戻り値の型はブラウザで実行する場合とnode.js上で実行する場合とで異なるため、関数の戻り値の型を参照する形式にしています。

/**
 * 最後に呼び出されたときに指定した処理を実行するdebounce関数を作成する
 * @param callback 関数
 * @param delay 判定遅延[ms]
 * @returns debounce
 */
export const debounce = <T extends (...args: never[]) => unknown>(callback: T, delay: number) => {
	let timerID: ReturnType<typeof setTimeout>;
	return (...args: Parameters<T>) => {
		clearTimeout(timerID);
		timerID = setTimeout(() => callback(...args), delay);
	};
};

throttle

throttleは連続で呼び出しがある場合に、特定の処理を呼び出す間隔を制限するテクニックです。
よくある使い方としては、画面のサイズ変更がされた場合に、画面の要素に対してサイズや位置などを再計算し適用する処理に用いられます。
例えば画面サイズに合わせてCanvas要素の大きさを再計算し、中身を再描画するなどです。

実行したい処理が重たい場合は特に、ユーザ操作時にパフォーマンスが著しく悪化することを防げます。

コード

/**
 * 連続した呼び出された場合に処理を実行する間隔を制限するthrottle関数を作成する
 * @param callback 関数
 * @param delay 間隔[ms]
 * @returns throttle
 */
export const throttle = <T extends (...args: never[]) => unknown>(callback: T, delay: number) => {
	let timerID: ReturnType<typeof setTimeout>;
	let lastTime: number;
	return (...args: Parameters<T>) => {
		const fireFn = () => {
			callback(...args);
			lastTime = Date.now();
		};
		if (lastTime == undefined) {
			fireFn();
		} else {
			clearTimeout(timerID);
			timerID = setTimeout(fireFn, delay - Date.now() + lastTime);
		}
	};
};