setTimeoutの基本 - JavaScriptで遅延処理を実装する方法

JavaScriptのsetTimeout関数を初心者向けに詳しく解説。基本的な使い方から実践的な応用例まで、非同期処理と遅延実行をマスターするための重要な知識を分かりやすく紹介します。

Learning Next 運営
32 分で読めます

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(); // 編集1
setTimeout(() => onUserEdit(), 1000); // 1秒後に編集2
setTimeout(() => 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アプリケーションを開発してみませんか?

関連記事