JavaScript | Web API:タイマー・スケジューリング - clearTimeout

JavaScript JavaScript
スポンサーリンク

clearTimeout は「さっき予約した“◯秒後”をなかったことにする」関数

clearTimeout は、setTimeout で予約した「あとで実行する処理」をキャンセルするための関数 です。

「3 秒後に実行して」と予約したけど、その 3 秒の間に状況が変わって
「やっぱり実行しなくていいや」となったときに使います。

アニメーションの中断、
「閉じる前に 5 秒待つ」ダイアログのキャンセル、
入力が続いている間は検索を実行しない(デバウンス)など、
「一度予約した“未来の処理”を取り消す」場面でよく登場します。


clearTimeout を使うための前提:ID をちゃんと持っておく

setTimeout の戻り値が「キャンセル用の鍵」になる

clearTimeout を理解するには、
まず setTimeout が「タイマー ID」を返す ことを押さえる必要があります。

const id = setTimeout(() => {
  console.log("3秒後に実行されるはずだった処理");
}, 3000);
JavaScript

この id が、
「この予約をキャンセルしたいときに必要な鍵」です。

clearTimeout は、この ID を受け取って、
「そのタイマーを無効にする」関数です。

clearTimeout(id);
JavaScript

ID をどこに保存するかが設計のポイント

実際のコードでは、
この ID をどこに置いておくかが大事になります。

関数のローカル変数に入れておくのか
モジュールの外側の変数にしておくのか
オブジェクトのプロパティとして持つのか

例えば、ボタンのクリックでタイマーを開始・キャンセルするなら、
外側に変数を用意しておくのが定番です。

let timerId = null;

function start() {
  timerId = setTimeout(() => {
    console.log("実行!");
  }, 3000);
}

function cancel() {
  if (timerId !== null) {
    clearTimeout(timerId);
    timerId = null;
  }
}
JavaScript

具体例 1:3 秒後に実行される処理をキャンセルする

シンプルな「開始」と「キャンセル」の組み合わせ

HTML をイメージします。

<button id="start">3秒後に実行</button>
<button id="cancel">キャンセル</button>
<div id="log"></div>

JavaScript はこう書けます。

const startButton = document.querySelector("#start");
const cancelButton = document.querySelector("#cancel");
const log = document.querySelector("#log");

let timerId = null;

startButton.addEventListener("click", () => {
  if (timerId !== null) {
    log.textContent = "すでに予約済みです";
    return;
  }

  log.textContent = "3秒後に実行を予約しました";

  timerId = setTimeout(() => {
    log.textContent = "処理を実行しました";
    timerId = null;
  }, 3000);
});

cancelButton.addEventListener("click", () => {
  if (timerId !== null) {
    clearTimeout(timerId);
    timerId = null;
    log.textContent = "予約をキャンセルしました";
  } else {
    log.textContent = "キャンセルする予約はありません";
  }
});
JavaScript

ここでのポイントは、
「今タイマーが動いているかどうか」を timerIdnull かどうかで管理していることです。

予約するたびに timerId に ID を入れる
キャンセルしたら clearTimeout して null に戻す

このパターンは、現場でもそのまま使える基本形です。


具体例 2:「入力が止まってから検索する」デバウンスでの clearTimeout

clearTimeout が本領発揮するパターン

clearTimeout が一番「効いてるな」と感じられるのが、
デバウンス(入力が止まってから処理する) のパターンです。

検索ボックスをイメージします。

<input id="search" placeholder="検索ワードを入力" />
<div id="status"></div>

JavaScript はこう。

const input = document.querySelector("#search");
const status = document.querySelector("#status");

let timerId = null;

input.addEventListener("input", () => {
  const keyword = input.value;

  if (timerId !== null) {
    clearTimeout(timerId); // 前の予約をキャンセル
  }

  status.textContent = "入力中…";

  timerId = setTimeout(() => {
    status.textContent = `「${keyword}」で検索します`;
    // ここで実際に fetch などで検索 API を叩く
  }, 500);
});
JavaScript

ここで起きていることを丁寧に言語化すると、

入力があるたびに「500ms 後に検索する」予約を入れる
ただし、その前に「前回の予約」を clearTimeout でキャンセルする
結果として、「最後の入力から 500ms 経ったときだけ検索が走る」

という動きになります。

この「前の予約をキャンセルして、最後のだけ残す」というのが、
clearTimeout の一番気持ちいい使い方です。


重要ポイント:clearTimeout は「すでに実行されたもの」は止められない

間に合うのは「まだ実行されていないタイマー」だけ

clearTimeout ができるのは、
「まだ実行されていないタイマーを無効にすること」 だけです。

例えば、こういうコードを考えます。

const id = setTimeout(() => {
  console.log("実行!");
}, 1000);

setTimeout(() => {
  clearTimeout(id);
  console.log("キャンセルしようとした");
}, 2000);
JavaScript

1 秒後に「実行!」を出す予約
2 秒後にキャンセルしようとする予約

この場合、1 秒後にはもうコールバックが実行されてしまっているので、
2 秒後に clearTimeout(id) を呼んでも何も起きません。

つまり、

「実行される前にキャンセルする」
というタイミング管理が大事になります。

「もう終わったタイマーの ID」を clearTimeout してもエラーにはならない

clearTimeout に「すでに終わったタイマーの ID」や「null」を渡しても、
エラーにはなりません(ただ何も起きないだけ)。

clearTimeout(null);      // 何も起きない
clearTimeout(999999999); // 何も起きない(存在しないID)
JavaScript

なので、実務では

「とりあえず clearTimeout(timerId) してから、新しいタイマーをセットする」

という書き方がよく使われます。

clearTimeout(timerId);
timerId = setTimeout(...);
JavaScript

これで、「前の予約が残っていても消す」「なくても問題ない」という安全な書き方になります。


setTimeout と clearTimeout をセットで設計する感覚

「予約」と「キャンセル」は常にペアで考える

setTimeout を使うときは、
「この予約をキャンセルしたくなる状況はあるか?」
を一緒に考えるクセをつけると、設計が一段うまくなります。

アニメーションの終了を遅らせる
→ ユーザーが途中で画面を閉じたら?

自動で閉じる通知を 5 秒後に消す
→ ユーザーが自分で閉じたら?

入力が止まってから検索する
→ まだ入力が続いているのに前の予約が残っていたら?

こういう「途中で状況が変わる」ケースを想像すると、
自然と clearTimeout の出番が見えてきます。

「状態」と一緒に管理するのがコツ

タイマー ID は、
「今どんな状態か」を表す一部として扱うと分かりやすいです。

タイマーが動いている → timerId に数値が入っている
タイマーが止まっている → timerIdnull

というふうに、「状態」として変数を扱うと、

二重にタイマーを起動しない
止めるべきときにちゃんと止める

といった制御がしやすくなります。


初心者として clearTimeout で本当に掴んでほしいこと

clearTimeout は「setTimeout で予約した処理をキャンセルする」ための関数
キャンセルするには、setTimeout の戻り値(タイマー ID)をちゃんと保存しておく必要がある
「前の予約をキャンセルして、最後のだけ残す」デバウンスのようなパターンで真価を発揮する
実行されてしまったタイマーは止められないので、「いつキャンセルするか」のタイミング設計が重要
clearTimeout に「終わった ID」や null を渡してもエラーにはならないので、まず消してから新しく予約する書き方がよく使われる

小さな練習として、

3 秒後に色が変わるボックス
→ その前にマウスを乗せたら clearTimeout でキャンセルする

みたいな UI を作ってみると、
「予約した未来をなかったことにする感覚」が、手触りとして掴めるはずです。

タイトルとURLをコピーしました