1. ホーム
  2. JavaScript

【JavaScript】setTimeout と setInterval の使い方|遅延・繰り返し実行と止める方法

Share

JavaScript で「数秒後に処理を実行したい」「一定間隔で同じ処理を繰り返したい」というとき、出番になるのが setTimeout()setInterval() です。この記事では、一定時間後に1回だけ実行する setTimeout() と、一定の間隔で繰り返し実行する setInterval() の使い方を、構文・引数・戻り値のタイマーID、そして clearTimeout() / clearInterval() で止める方法まで順に解説します。数秒後に通知を消す、カウントダウンや時計を動かすといった実践例に加え、遅延時間が正確でない点やループ内で使うときの落とし穴など、初心者がつまずきやすいポイントも具体的に取り上げます。

setTimeout と setInterval でできること

JavaScript はコードを上から順に実行しますが、setTimeout()setInterval() を使うと「時間に関する処理」を組み込めます。どちらもブラウザ(や Node.js)が提供するタイマー機能で、指定した時間に合わせてコールバック関数を呼び出してくれます。

違いはシンプルで、setTimeout() は「指定した時間が経ったら1回だけ実行」、setInterval() は「指定した間隔ごとに繰り返し実行」します。たとえば数秒後にメッセージを消すなら setTimeout()、1秒ごとに時計を更新し続けるなら setInterval()、といった使い分けになります。

関数動作
setTimeout()指定した時間が経過した後、コールバックを1回だけ実行する
setInterval()指定した間隔ごとに、コールバックを繰り返し実行する

基本の使い方と構文

まずは setTimeout() の最小の例です。第1引数に実行したい関数、第2引数に待つ時間(ミリ秒)を渡します。1000 ミリ秒=1秒なので、次のコードは約2秒後にメッセージを表示します。

main.js
// 2000 ミリ秒(2秒)後にコールバックを1回だけ実行する
setTimeout(() => {
  console.log("2秒経ちました");
}, 2000);

setInterval() も書き方はほとんど同じで、第1引数にコールバック、第2引数に間隔(ミリ秒)を渡します。次のコードは1秒ごとにメッセージを表示し続けます。後で説明する方法で止めない限り、ずっと繰り返される点に注意してください。

main.js
// 1000 ミリ秒(1秒)ごとにコールバックを繰り返し実行する
setInterval(() => {
  console.log("1秒ごとに呼ばれます");
}, 1000);

引数の意味

どちらの関数も引数の構成は共通です。第3引数以降に値を渡すと、それがそのままコールバックの引数として渡されます。各引数の意味を次の表にまとめました。

引数説明
第1引数(コールバック関数)時間が来たときに実行する関数。必須
第2引数(遅延・間隔ミリ秒)待つ時間(setTimeout)または繰り返す間隔(setInterval)をミリ秒で指定。省略すると 0 とみなされる
第3引数以降(追加の引数)コールバックに渡したい値。指定した順にコールバックの引数として渡される

第3引数以降を使うと、コールバックに値を渡せます。たとえば次のように書くと、namecount がそのまま関数の引数として渡ります。

main.js
function greet(name, count) {
  console.log(`${name} さん、${count} 回目の呼び出しです`);
}

// 第3引数以降が greet の引数になる("田中", 1 が渡る)
setTimeout(greet, 1000, "田中", 1);

戻り値はタイマーID

setTimeout()setInterval() は、呼び出すと「タイマーID」という数値を返します。これは登録したタイマーを識別するための番号で、後からそのタイマーを止めるときに使います。止める必要があるなら、戻り値を必ず変数に保存しておきましょう。

main.js
// 戻り値(タイマーID)を変数に保存しておく
const timerId = setInterval(() => {
  console.log("実行中");
}, 1000);

console.log(timerId); // タイマーIDを表す数値が表示される

clearTimeout と clearInterval で止める

登録したタイマーは、対応する解除関数にタイマーIDを渡すことで止められます。setTimeout() で登録したものは clearTimeout()setInterval() で登録したものは clearInterval() で解除します。

解除関数対象引数
clearTimeout(id)setTimeout() で登録したタイマー解除したいタイマーID
clearInterval(id)setInterval() で登録したタイマー解除したいタイマーID

setTimeout() はまだ実行されていない(待機中の)タイマーであれば、clearTimeout() でキャンセルできます。次の例では、2秒後に実行する予定だった処理を、その前に取り消しています。

main.js
const timerId = setTimeout(() => {
  console.log("この処理は実行されない");
}, 2000);

// 実行される前に取り消す
clearTimeout(timerId);

setInterval() は止めない限り繰り返し続けるので、clearInterval() で明示的に停止することが特に重要です。次の例は、1秒ごとにカウントを増やし、5回に達したら自分自身を止めます。コールバックの中から clearInterval() を呼べる点を押さえておきましょう。

main.js
let count = 0;

const timerId = setInterval(() => {
  count += 1;
  console.log(`${count} 回目`);

  // 5回繰り返したら停止する
  if (count >= 5) {
    clearInterval(timerId);
    console.log("停止しました");
  }
}, 1000);

数秒後に通知メッセージを消す

ここからは実践的な例を見ていきます。まずは setTimeout() の定番、「表示した通知を数秒後に自動で消す」処理です。要素を表示してから setTimeout() で数秒待ち、時間が来たら非表示にする、という流れです。

main.js
const toast = document.querySelector("#toast");

function showToast(message) {
  toast.textContent = message;
  toast.style.display = "block";

  // 3秒後に自動で非表示にする
  setTimeout(() => {
    toast.style.display = "none";
  }, 3000);
}

showToast("保存しました");

setInterval でカウントダウンを動かす

次は setInterval() を使ったカウントダウンです。1秒ごとに数値を1ずつ減らし、画面の表示を更新します。0になったら clearInterval() でタイマー自身を止めるのがポイントです。下のデモはスタート・ストップ・リセットができるようにしてあります。タブを切り替えてコードを確認し、ボタンを押して動きを試してみてください。

<div class="demo">
  <p class="display" id="display">10</p>
  <div class="buttons">
    <button id="start" type="button">スタート</button>
    <button id="stop" type="button">ストップ</button>
    <button id="reset" type="button">リセット</button>
  </div>
</div>
body {
  margin: 0;
  font-family: sans-serif;
  color: #333;
}

.demo {
  padding: 24px;
  text-align: center;
}

.display {
  font-size: 64px;
  font-weight: bold;
  margin: 0 0 16px;
  color: #4f46e5;
}

.buttons {
  display: flex;
  gap: 8px;
  justify-content: center;
}

button {
  padding: 8px 16px;
  border: none;
  border-radius: 6px;
  background: #4f46e5;
  color: #fff;
  font-weight: bold;
  cursor: pointer;
}

button:disabled {
  background: #b4b0e8;
  cursor: not-allowed;
}
const display = document.querySelector("#display");
const startBtn = document.querySelector("#start");
const stopBtn = document.querySelector("#stop");
const resetBtn = document.querySelector("#reset");

let count = 10;
let timerId = null; // タイマーIDの保管場所(停止に使う)

function start() {
  // すでに動いていれば二重起動を防ぐ
  if (timerId !== null) return;
  startBtn.disabled = true;

  timerId = setInterval(() => {
    count -= 1;
    display.textContent = count;

    // 0 になったら自分自身を止める
    if (count <= 0) {
      clearInterval(timerId);
      timerId = null;
      startBtn.disabled = false;
    }
  }, 1000);
}

function stop() {
  clearInterval(timerId);
  timerId = null;
  startBtn.disabled = false;
}

function reset() {
  stop();
  count = 10;
  display.textContent = count;
}

startBtn.addEventListener("click", start);
stopBtn.addEventListener("click", stop);
resetBtn.addEventListener("click", reset);
Preview

同じ仕組みで時計も作れます。setInterval() の中で new Date() から現在時刻を取り出し、1秒ごとに表示を書き換えれば、リアルタイムに動く時計になります。

clock.js
const clock = document.querySelector("#clock");

function update() {
  // 現在時刻を「時:分:秒」の形式で表示する
  clock.textContent = new Date().toLocaleTimeString();
}

update();                 // 最初の1回はすぐ表示する
setInterval(update, 1000); // 以降は1秒ごとに更新する

遅延時間は「最低限の待ち時間」でしかない

タイマーを使ううえで最初に知っておきたいのが、指定した時間はあくまで「最低でもこれだけ待つ」という目安だという点です。タイマーの仕組みを正しく理解しておくと、思わぬ挙動に戸惑わずに済みます。

指定時間ぴったりには実行されない

JavaScript は基本的に1つの処理を順番にこなしていくため、指定時間が来てもメインの処理が動いている間はコールバックを実行できません。タイマーは「指定時間が経ったら実行待ちの列に並ぶ」だけで、実際に呼ばれるのは手が空いたタイミングです。そのため setTimeout(fn, 1000) と書いても、ちょうど1000ミリ秒後に呼ばれるとは限らず、わずかに遅れることがあります。「正確な時刻に合わせたい」という用途には向かないと覚えておきましょう。

setInterval は処理が重いと間隔が詰まる

同じ理由で、setInterval() もコールバックの処理が間隔より長くかかると、次の実行が遅れたり、待機していた呼び出しが連続して走ったりすることがあります。重い処理を短い間隔で繰り返すと、こうした「詰まり」が起きやすくなります。

これを避けたいときは、setInterval() の代わりに setTimeout() を再帰的に呼び出す方法がよく使われます。1回の処理が終わってから次のタイマーをセットするので、処理が重なりにくく、間隔を安定させやすくなります。

main.js
function tick() {
  // ここで何らかの処理を行う
  console.log("処理を実行");

  // 処理が終わってから次のタイマーをセットする
  setTimeout(tick, 1000);
}

// 最初の1回を起動する
setTimeout(tick, 1000);

ループの中で setTimeout を使うときの注意

「1秒おきに順番に処理したい」と考えて、for ループの中で setTimeout() を使うと、変数のスコープによっては意図しない結果になります。これは varlet の違いに関わる、有名なつまずきポイントです。

var で宣言したカウンタ変数はループ全体で1つだけ共有されます。setTimeout() のコールバックが実際に動くのはループが終わった後なので、その時点の i の値(最終値)を全部のコールバックが参照してしまいます。

main.js
// var は1つの変数を共有してしまう
for (var i = 0; i < 3; i++) {
  setTimeout(() => {
    console.log(i); // 0,1,2 ではなく「3」が3回表示される
  }, i * 1000);
}

解決策はシンプルで、varlet に変えるだけです。let はループの繰り返しごとに新しい変数が作られるため、各コールバックがその時点の値を覚えていてくれます(これがクロージャの働きです)。特別な理由がなければ let を使いましょう。

main.js
// let なら繰り返しごとに別々の変数になる
for (let i = 0; i < 3; i++) {
  setTimeout(() => {
    console.log(i); // 0,1,2 と順に表示される
  }, i * 1000);
}

タイマーの二重起動を防ぐ

ボタンを押すたびに setInterval() を呼ぶような作りにしていると、押すごとに新しいタイマーが増えていき、同じ処理が何重にも走ってしまうことがあります。たとえばカウントダウンならスタートボタンを2回押すと2倍の速さで進む、といった不具合です。

これを防ぐには、タイマーIDを変数で管理し、すでに動いているなら新しく登録しない、あるいは新しく始める前に必ず clearInterval() で止める、という対策が有効です。先ほどのカウントダウンのデモでも、timerIdnull でないときは start() を途中で抜けることで二重起動を防いでいました。止めるときは clearInterval() を呼んだうえで、変数を null に戻して「今は動いていない」状態を明示しておくと管理しやすくなります。

まとめ

setTimeout() は指定時間後に1回だけ、setInterval() は一定間隔で繰り返しコールバックを実行する関数です。どちらも第1引数にコールバック、第2引数にミリ秒を渡し、第3引数以降でコールバックへ値を渡せます。戻り値のタイマーIDを保存しておけば、clearTimeout() / clearInterval() で後から止められます。通知の自動非表示には setTimeout()、カウントダウンや時計の更新には setInterval() が向いています。

使ううえでは、指定時間はあくまで最低限の待ち時間で正確ではないこと、setInterval() は重い処理で詰まりやすく setTimeout() の再帰呼び出しで代替できること、ループ内では let を使ってクロージャの罠を避けること、そして必ず clear 系で止めて二重起動を防ぐこと、を押さえておきましょう。これらを意識すれば、時間に関する処理を安定して扱えるようになります。

参考ページ