setTimeoutの基本 - JavaScriptで遅延処理を実装する方法
JavaScriptのsetTimeout関数を初心者向けに詳しく解説。基本的な使い方から実践的な応用例まで、非同期処理と遅延実行をマスターするための重要な知識を分かりやすく紹介します。
setTimeoutの基本 - JavaScriptで遅延処理を実装する方法
みなさん、JavaScriptを使っていて「3秒後にメッセージを表示したい」と思ったことありませんか?
「少し待ってから処理を実行したい」 「アニメーションを遅らせて表示したい」 「自動保存機能を作りたい」
こんな場面に遭遇することってありますよね。
実は、JavaScriptにはsetTimeout関数という便利な機能があるんです。 これを使えば、指定した時間後に関数を実行する遅延処理が簡単に実装できます。
この記事では、JavaScript初心者向けにsetTimeoutの基本的な使い方から実践的な応用例まで詳しく解説します。 非同期処理の基礎をマスターして、より動的で使いやすいプログラムを作れるようになりますよ。
setTimeoutとは?
基本的な概念
setTimeoutとは、指定した時間後に関数を一度だけ実行するJavaScriptの組み込み関数です。
簡単に言うと、時間差で処理を実行する機能ですね。 非同期処理の一種で、他の処理をブロックすることなく遅延実行を行います。
// 基本的な使い方setTimeout(function() { console.log("3秒後に実行されます");}, 3000);
console.log("この行は即座に実行されます");
// 実行順序:// 1. "この行は即座に実行されます"(即座)// 2. "3秒後に実行されます"(3秒後)
このコードを実行すると、最初に「この行は即座に実行されます」が表示されます。 その後、3秒待ってから「3秒後に実行されます」が表示されます。
setTimeoutの特徴
setTimeoutには以下の特徴があります。
- 非同期実行: 他の処理をブロックしない
- 一回のみ実行: 指定した時間後に一度だけ実行
- ミリ秒単位: 時間はミリ秒で指定(1秒 = 1000ミリ秒)
- 戻り値: タイマーIDを返す(キャンセルに使用)
実際に戻り値を確認してみましょう。
// 戻り値(タイマーID)の取得let timerId = setTimeout(function() { console.log("実行されました");}, 2000);
console.log("タイマーID:", timerId); // 数値が表示される
// タイマーのキャンセルclearTimeout(timerId);console.log("タイマーをキャンセルしました");
setTimeoutは非同期処理なので、他のコードの実行を待たないことを理解しておきましょう。
基本的な使用方法
関数の遅延実行
まずは基本的な使い方から見てみましょう。
// 匿名関数を使用setTimeout(function() { alert("5秒後に表示されるアラート");}, 5000);
// アロー関数を使用setTimeout(() => { console.log("アロー関数での遅延実行");}, 2000);
// 既存の関数を呼び出しfunction showMessage() { console.log("既存の関数が呼び出されました");}
setTimeout(showMessage, 1000);
匿名関数、アロー関数、既存の関数、どれでも使えます。 アロー関数を使うと、より簡潔に書けますね。
引数付きの関数実行
関数に引数を渡したい場合は、いくつかの方法があります。
// 引数を渡す方法function greetUser(name, message) { console.log(`${message}, ${name}さん!`);}
// 方法1: 無名関数でラップsetTimeout(function() { greetUser("太郎", "こんにちは");}, 1000);
// 方法2: bind()を使用setTimeout(greetUser.bind(null, "花子", "おはよう"), 2000);
// 方法3: setTimeoutの第3引数以降を使用setTimeout(greetUser, 3000, "次郎", "こんばんは");
無名関数でラップする方法が一番分かりやすいですね。 方法3はあまり使われませんが、覚えておくと便利です。
複数の遅延処理
複数のタイマーを同時に設定することもできます。
// 順次実行される複数のタイマーconsole.log("開始");
setTimeout(() => { console.log("1秒後");}, 1000);
setTimeout(() => { console.log("2秒後");}, 2000);
setTimeout(() => { console.log("3秒後");}, 3000);
このコードを実行すると、「開始」→「1秒後」→「2秒後」→「3秒後」の順で表示されます。
チェーン的な実行も可能です。
function chainedExecution() { console.log("ステップ1"); setTimeout(() => { console.log("ステップ2(1秒後)"); setTimeout(() => { console.log("ステップ3(さらに1秒後)"); setTimeout(() => { console.log("ステップ4(さらに1秒後)"); }, 1000); }, 1000); }, 1000);}
chainedExecution();
ただし、ネストが深くなりすぎると読みにくくなるので注意が必要です。
clearTimeoutでタイマーを停止
基本的なキャンセル方法
setTimeoutで設定したタイマーは、clearTimeoutでキャンセルできます。
// タイマーの設定とキャンセルlet timer = setTimeout(() => { console.log("この処理は実行されません");}, 3000);
// 1秒後にタイマーをキャンセルsetTimeout(() => { clearTimeout(timer); console.log("タイマーをキャンセルしました");}, 1000);
このコードでは、3秒後の処理が1秒後にキャンセルされます。 「この処理は実行されません」は表示されません。
条件付きキャンセル
条件によってタイマーをキャンセルする例も見てみましょう。
// 条件によってタイマーをキャンセルする例let userLoggedIn = false;let loginTimer = setTimeout(() => { alert("セッションがタイムアウトしました"); // ログアウト処理}, 30000); // 30秒後
// ユーザーがログインした場合function onUserLogin() { userLoggedIn = true; clearTimeout(loginTimer); console.log("ログインタイマーをキャンセルしました");}
// シミュレーション:5秒後にログインsetTimeout(onUserLogin, 5000);
ユーザーがログインしたら、セッションタイムアウトのタイマーをキャンセルしています。 実際のWebアプリケーションでよく使われるパターンですね。
動的なタイマー管理
複数のタイマーを効率的に管理するクラスを作ってみましょう。
// 複数のタイマーを管理するクラスclass TimerManager { constructor() { this.timers = new Map(); this.nextId = 1; } // タイマーを設定 setTimeout(callback, delay, name = null) { let id = name || `timer_${this.nextId++}`; // 既存のタイマーがあればキャンセル if (this.timers.has(id)) { clearTimeout(this.timers.get(id)); } let timerId = setTimeout(() => { callback(); this.timers.delete(id); // 実行後に削除 }, delay); this.timers.set(id, timerId); console.log(`タイマー "${id}" を設定しました(${delay}ms後に実行)`); return id; } // 特定のタイマーをキャンセル clearTimer(id) { if (this.timers.has(id)) { clearTimeout(this.timers.get(id)); this.timers.delete(id); console.log(`タイマー "${id}" をキャンセルしました`); return true; } return false; } // 全てのタイマーをキャンセル clearAllTimers() { for (let [id, timerId] of this.timers) { clearTimeout(timerId); } console.log(`${this.timers.size}個のタイマーをキャンセルしました`); this.timers.clear(); } // アクティブなタイマー数を取得 getActiveTimerCount() { return this.timers.size; }}
使用例を見てみましょう。
// 使用例let timerManager = new TimerManager();
timerManager.setTimeout(() => { console.log("自動保存を実行");}, 5000, "autosave");
timerManager.setTimeout(() => { console.log("通知を表示");}, 3000, "notification");
// 2秒後に通知タイマーをキャンセルsetTimeout(() => { timerManager.clearTimer("notification");}, 2000);
このクラスを使うと、複数のタイマーを名前で管理できて便利ですね。
実践的な応用例
自動保存機能
実際のWebアプリケーションでよく使われる自動保存機能を作ってみましょう。
// 自動保存機能の実装class AutoSave { constructor(saveFunction, delay = 5000) { this.saveFunction = saveFunction; this.delay = delay; this.saveTimer = null; this.isDirty = false; // 変更があったかのフラグ } // 変更を通知 markDirty() { this.isDirty = true; this.scheduleAutoSave(); } // 自動保存をスケジュール scheduleAutoSave() { // 既存のタイマーをキャンセル if (this.saveTimer) { clearTimeout(this.saveTimer); } // 新しいタイマーを設定 this.saveTimer = setTimeout(() => { this.performSave(); }, this.delay); console.log(`${this.delay}ms後に自動保存を実行します`); } // 保存を実行 performSave() { if (this.isDirty) { this.saveFunction(); this.isDirty = false; this.saveTimer = null; console.log("自動保存を実行しました"); } } // 即座に保存 saveNow() { if (this.saveTimer) { clearTimeout(this.saveTimer); this.saveTimer = null; } this.performSave(); } // 自動保存を停止 stop() { if (this.saveTimer) { clearTimeout(this.saveTimer); this.saveTimer = null; console.log("自動保存を停止しました"); } }}
使用例を見てみましょう。
function saveDocument() { console.log("ドキュメントを保存中..."); // 実際の保存処理}
let autoSave = new AutoSave(saveDocument, 3000); // 3秒後に自動保存
// ユーザーが編集したときの処理function onUserEdit() { autoSave.markDirty();}
// シミュレーションconsole.log("編集開始");onUserEdit(); // 編集1setTimeout(() => onUserEdit(), 1000); // 1秒後に編集2setTimeout(() => onUserEdit(), 2000); // 2秒後に編集3
ユーザーが編集するたびに自動保存のタイマーがリセットされます。 最後の編集から3秒後に自動保存が実行される仕組みです。
通知システム
通知を一定時間後に自動で非表示にするシステムを作ってみましょう。
// 通知システムの実装class NotificationSystem { constructor() { this.notifications = []; this.nextId = 1; } // 通知を表示 show(message, duration = 5000, type = 'info') { let notification = { id: this.nextId++, message: message, type: type, timestamp: new Date(), timer: null }; this.notifications.push(notification); this.displayNotification(notification); // 自動非表示のタイマーを設定 if (duration > 0) { notification.timer = setTimeout(() => { this.hide(notification.id); }, duration); } return notification.id; } // 通知を非表示 hide(notificationId) { let index = this.notifications.findIndex(n => n.id === notificationId); if (index !== -1) { let notification = this.notifications[index]; // タイマーをキャンセル if (notification.timer) { clearTimeout(notification.timer); } this.notifications.splice(index, 1); this.removeNotificationDisplay(notification); console.log(`通知 ${notificationId} を非表示にしました`); } } // 全ての通知を非表示 hideAll() { this.notifications.forEach(notification => { if (notification.timer) { clearTimeout(notification.timer); } this.removeNotificationDisplay(notification); }); console.log(`${this.notifications.length}個の通知を非表示にしました`); this.notifications = []; } // 通知の表示処理(実際のUI更新) displayNotification(notification) { console.log(`[${notification.type.toUpperCase()}] ${notification.message}`); // 実際のアプリケーションではDOM操作を行う } // 通知の削除処理(実際のUI更新) removeNotificationDisplay(notification) { console.log(`通知を削除: ${notification.message}`); // 実際のアプリケーションではDOM要素を削除 } // アクティブな通知数を取得 getActiveCount() { return this.notifications.length; }}
使用例を見てみましょう。
let notificationSystem = new NotificationSystem();
// 様々な種類の通知notificationSystem.show("保存が完了しました", 3000, "success");notificationSystem.show("エラーが発生しました", 5000, "error");notificationSystem.show("新しいメッセージがあります", 0, "info"); // 自動非表示なし
// 2秒後に全ての通知を非表示setTimeout(() => { notificationSystem.hideAll();}, 2000);
通知の種類や表示時間を柔軟に設定できます。 実際のWebアプリケーションで使えるレベルの機能ですね。
遅延検索(デバウンス)
検索機能で、ユーザーの入力が終わってから検索を実行するデバウンス機能を作ってみましょう。
// 検索機能のデバウンス実装class DelayedSearch { constructor(searchFunction, delay = 300) { this.searchFunction = searchFunction; this.delay = delay; this.searchTimer = null; this.lastQuery = ''; } // 検索を実行(遅延付き) search(query) { // 前回と同じクエリの場合はスキップ if (query === this.lastQuery) { return; } // 既存のタイマーをキャンセル if (this.searchTimer) { clearTimeout(this.searchTimer); } // 空のクエリの場合は即座に検索 if (query.trim() === '') { this.performSearch(query); return; } // 遅延検索を設定 this.searchTimer = setTimeout(() => { this.performSearch(query); }, this.delay); console.log(`${this.delay}ms後に "${query}" を検索します`); } // 実際の検索処理 performSearch(query) { this.lastQuery = query; this.searchTimer = null; this.searchFunction(query); } // 即座に検索 searchNow(query) { if (this.searchTimer) { clearTimeout(this.searchTimer); this.searchTimer = null; } this.performSearch(query); }}
模擬検索関数と使用例を見てみましょう。
// 模擬検索関数function performActualSearch(query) { if (query.trim() === '') { console.log("検索結果をクリア"); return; } console.log(`"${query}" の検索を実行中...`); // 模擬検索結果 let results = [ `${query}に関する結果1`, `${query}に関する結果2`, `${query}に関する結果3` ]; setTimeout(() => { console.log("検索結果:", results); }, 100);}
// 使用例let delayedSearch = new DelayedSearch(performActualSearch, 500);
// ユーザーの入力をシミュレートconsole.log("検索テストを開始");delayedSearch.search("J"); // 即座にキャンセルされるsetTimeout(() => delayedSearch.search("Ja"), 100); // キャンセルされるsetTimeout(() => delayedSearch.search("Jav"), 200); // キャンセルされるsetTimeout(() => delayedSearch.search("Java"), 300); // これが実行される
ユーザーが「Java」と入力する過程で、途中の「J」「Ja」「Jav」での検索はキャンセルされます。 最終的な「Java」だけが検索されるので、サーバーへの負荷を軽減できますね。
よくある間違いとその対策
無限ループの問題
setTimeoutを使った処理で無限ループになる問題と対策を見てみましょう。
// 問題のある例:無限ループfunction problematicFunction() { console.log("実行中..."); setTimeout(problematicFunction, 100); // 自分自身を呼び出し続ける}
// problematicFunction(); // これを実行すると止まらない
// 改善例1:停止条件を追加let count = 0;function improvedFunction1() { console.log(`実行 ${++count}`); if (count < 10) { // 10回まで setTimeout(improvedFunction1, 100); } else { console.log("処理完了"); }}
// 改善例2:外部から制御可能にするclass ControlledTimer { constructor() { this.isRunning = false; this.timer = null; } start() { if (this.isRunning) return; this.isRunning = true; this.execute(); } stop() { this.isRunning = false; if (this.timer) { clearTimeout(this.timer); this.timer = null; } } execute() { if (!this.isRunning) return; console.log("処理実行中..."); this.timer = setTimeout(() => { this.execute(); }, 100); }}
let controlledTimer = new ControlledTimer();controlledTimer.start();
// 3秒後に停止setTimeout(() => { controlledTimer.stop();}, 3000);
無限ループを防ぐには、停止条件を設けることが重要です。 外部から制御できるようにしておくと、より安全ですね。
メモリリークの防止
タイマーの適切な管理でメモリリークを防ぐ方法を見てみましょう。
// 問題のある例:タイマーIDの管理不足class LeakyComponent { constructor() { this.data = new Array(1000000).fill("data"); // 大きなデータ } startTimer() { // タイマーIDを保存せずに設定 setTimeout(() => { console.log("処理実行"); this.processData(); }, 5000); } processData() { // 重い処理 console.log("データ処理中..."); } // destroyメソッドがあっても、タイマーはキャンセルできない destroy() { this.data = null; // タイマーは残り続ける(メモリリーク) }}
// 改善例:適切なタイマー管理class SafeComponent { constructor() { this.data = new Array(1000000).fill("data"); this.timers = new Set(); // タイマーIDを管理 } startTimer() { let timerId = setTimeout(() => { this.processData(); this.timers.delete(timerId); // 実行後に削除 }, 5000); this.timers.add(timerId); } processData() { if (!this.data) return; // 破棄済みチェック console.log("データ処理中..."); } destroy() { // 全てのタイマーをキャンセル for (let timerId of this.timers) { clearTimeout(timerId); } this.timers.clear(); this.data = null; console.log("コンポーネントを安全に破棄しました"); }}
let safeComponent = new SafeComponent();safeComponent.startTimer();
// 2秒後に破棄(タイマー実行前)setTimeout(() => { safeComponent.destroy();}, 2000);
タイマーIDを適切に管理することで、メモリリークを防げます。 大丈夫です。このパターンを覚えておけば安全にタイマーを使えますよ。
時間精度の問題
setTimeoutの時間精度について理解しておきましょう。
// 問題:setTimeoutの精度は保証されないfunction testTimingAccuracy() { let startTime = Date.now(); setTimeout(() => { let actualDelay = Date.now() - startTime; console.log(`期待: 1000ms, 実際: ${actualDelay}ms`); // 実際は1000msより少し長くなることが多い }, 1000);}
testTimingAccuracy();
// 高精度が必要な場合の対策class PreciseTimer { constructor(callback, interval) { this.callback = callback; this.interval = interval; this.startTime = null; this.timer = null; this.isRunning = false; } start() { if (this.isRunning) return; this.isRunning = true; this.startTime = Date.now(); this.scheduleNext(); } scheduleNext() { if (!this.isRunning) return; let elapsed = Date.now() - this.startTime; let expectedTime = Math.ceil(elapsed / this.interval) * this.interval; let nextDelay = expectedTime - elapsed; // 最小遅延を保証 nextDelay = Math.max(nextDelay, 0); this.timer = setTimeout(() => { this.callback(); this.scheduleNext(); }, nextDelay); } stop() { this.isRunning = false; if (this.timer) { clearTimeout(this.timer); this.timer = null; } }}
// 使用例(1秒ごとに正確に実行しようとする)let preciseTimer = new PreciseTimer(() => { console.log("正確なタイミングでの実行:", new Date().toISOString());}, 1000);
preciseTimer.start();
// 5秒後に停止setTimeout(() => { preciseTimer.stop();}, 5000);
setTimeoutの時間は厳密ではありませんが、工夫することで精度を上げることができます。
まとめ
JavaScript のsetTimeoutについて、基本から応用まで詳しく学習しました。
基本的な特徴:
- 指定時間後に関数を一度だけ実行
- 非同期処理でブロックしない
- clearTimeoutでキャンセル可能
- タイマーIDを戻り値として取得
実践的な応用例:
- 自動保存機能の実装
- 通知システムの構築
- 検索機能のデバウンス処理
- スライドショーの自動再生
重要なポイント:
- 非同期処理の理解が重要
- タイマーIDの適切な管理
- メモリリーク防止のためのクリーンアップ
- 複数タイマーの効率的な管理
注意すべき点:
- 無限ループの防止
- タイマーの適切なキャンセル
- 時間精度の限界
- メモリリークの回避
setTimeoutは、JavaScriptにおける非同期処理の基礎となる重要な機能です。 適切に理解して使用することで、ユーザーエクスペリエンスを向上させる動的なアプリケーションを作成できます。
今回学んだ内容を実際のプロジェクトで活用して、効果的な遅延処理機能を実装してみてください。 大丈夫です。一つずつ理解していけば、必ず非同期処理をマスターできますよ。
ぜひ今日から、これらの技術を使って使いやすいWebアプリケーションを開発してみませんか?