【JavaScript】処理を一時停止させる「SLEEP関数」6つの作り方

WebページやWebアプリを作っていると、「特定の時間だけ処理を待たせたい」という場面が出てくることがあります。例えば、「ボタンを押したら、2秒後にメッセージを表示する」といった具合です。このような「待ち」を実現する関数を、ここでは便宜上「SLEEP関数」と呼びます。

JavaScriptには、そのまま使える sleep() という命令はありませんが、いくつかの方法を組み合わせて同じような機能を作ることができます。ここでは、主な6つの方法を紹介し、それぞれの仕組み、メリット・デメリット、そして「どんな時に使うのが良いか」を分かりやすく解説します。

1. setTimeout を使う方法(一番おすすめ!)

これは、JavaScriptでSLEEP機能を実現するための、最も一般的で推奨される方法です。

仕組み

  1. setTimeout(コールバック関数, 待つ時間)は、「指定した時間(ミリ秒)が経過したら、指定した関数(コールバック関数)を実行してね」とブラウザにお願いする機能です。
  2. これを利用して、「指定時間後に『待つのをやめるよ』という合図を送る」ようにします。
  3. Promise という「非同期処理(時間がかかる処理)の結果を扱うための仕組み」と、async/await という「Promiseの結果を待つための書き方」を組み合わせます。
  4. sleep 関数は Promise を返し、await sleep(時間) と書くことで、setTimeout で指定した時間が経過するまで、その後の処理の実行を待つことができます。

コード例

// sleep関数を定義
function sleep(ms) {
  // Promiseを作成し、setTimeoutで指定時間後にresolve()を呼ぶ
  return new Promise(resolve => setTimeout(resolve, ms));
}

// sleep関数を使う例 (async関数の中でawaitを使う)
async function myFunction() {
  console.log("処理開始");
  await sleep(2000); // 2000ミリ秒 = 2秒間待つ
  console.log("処理再開!");
}

myFunction();

メリット

  • 他の処理を邪魔しない(非同期):これが最大のメリットです。await sleep() で待っている間も、ブラウザは他の作業(例えば、ユーザーのクリック操作を受け付けたり、アニメーションを動かしたり)を続けることができます。ページが固まることがありません。
  • コードが読みやすいasync/await を使うことで、まるで普通の命令のように「待つ」処理を書くことができます。

デメリット

  • async/await の理解が必要async キーワードを関数につけ、await キーワードで sleep 関数を呼び出す、というお作法を覚える必要があります。ただ、これは現代のJavaScriptでは非常によく使われる基本的な書き方なので、覚えて損はありません。

使いどころ

  • ほとんどの「待ち」処理:ユーザー操作への応答、APIからのデータ取得後、アニメーションの合間など、Web開発におけるほとんどの「一時停止」の場面でこの方法が最適です。迷ったら、まずこの方法を使いましょう。

2. Date.now() を使う方法(非推奨!)

これは、コードは短く見えますが、絶対に避けるべき方法です。

仕組み

  1. Date.now() は、現在の時刻をミリ秒単位の数値で返します。
  2. 処理開始時の時刻を記録しておきます。
  3. while ループを使って、「現在時刻と開始時刻の差が、指定した待ち時間より小さい間は、何もせずにループを続ける」という処理を行います。
  4. 指定時間が経過するとループを抜け、次の処理に進みます。

コード例

function sleep(ms) {
  const startTime = Date.now();
  // 指定時間経つまで、ひたすら現在時刻を確認し続ける
  while (Date.now() - startTime < ms) {
    // 何もしない(CPUはフル回転!)
  }
}

// 使用例
console.log("処理開始");
sleep(2000); // 2秒間、ブラウザが応答しなくなる可能性大!
console.log("処理再開");

メリット

  • async/await を使わなくて済む(ように見える)。

デメリット

  • CPUを無駄に使い続ける(他の処理が完全に止まる):while ループが実行されている間、JavaScriptエンジンはその計算(現在時刻のチェック)にかかりっきりになります。これにより、ブラウザ全体がフリーズし、ユーザーはクリックもスクロールもできなくなります。これは非常に悪いユーザー体験につながります。
  • 効率が悪い:コンピューターのリソースを無駄に消費します。

使いどころ

  • 基本的にありません。 学習目的で「こういう書き方もある」と知っておくのは良いですが、実際のWebサイトやアプリでこの方法を使うのは絶対にやめましょう。

3. setInterval を使う方法

setTimeout と似ていますが、少しだけ仕組みが異なります。

仕組み

  1. setInterval(コールバック関数, 時間間隔)は、「指定した時間間隔ごとに、指定した関数を繰り返し実行してね」とブラウザにお願いする機能です。
  2. これを利用し、「指定時間後に一度だけ実行される処理」の中で、setInterval を停止(clearInterval)し、Promise を解決(待機終了の合図を送る)します。
  3. setTimeout と同様に、Promise と async/await を使って待機を実現します。

コード例

function sleep(ms) {
  return new Promise(resolve => {
    // 指定時間後に実行されるタイマーを設定
    const interval = setInterval(() => {
      // タイマーを停止
      clearInterval(interval);
      // 待機終了の合図を送る
      resolve();
    }, ms);
  });
}

// 使用例 (async/awaitはsetTimeoutと同じ)
async function myFunction() {
  console.log("処理開始");
  await sleep(2000); // 2秒待つ
  console.log("処理再開");
}

myFunction();

メリット

  • setTimeout と同じく、他の処理を邪魔しません(非同期)

デメリット

  • 後片付けが必要:setInterval は本来繰り返し実行するものなので、不要になったら clearInterval で明示的に停止させる必要があります。コードが少しだけ複雑になります。
  • 一度きりの待機には不自然:「指定時間後に一度だけ何かする」という目的なら、setTimeout の方が素直で分かりやすいです。

使いどころ

  • 基本的なSLEEP機能には setTimeout の方が適しています。setInterval は、本来「定期的に何かをチェックし、条件が満たされたら待機を終了する」といった、少し複雑な待機処理に応用できるかもしれませんが、通常は setTimeout で十分です。

4. requestAnimationFrame を使う方法

これは、主にアニメーションなど、ブラウザの画面描画と連携したい場合に使う方法です。ミリ秒ではなく、「フレーム数」で待機時間を指定します。

仕組み

  1. requestAnimationFrame(コールバック関数)は、「次の画面描画のタイミングで、指定した関数を実行してね」とブラウザにお願いする機能です。これは通常、1秒間に約60回(60fps)呼ばれます。
  2. これを利用し、「指定したフレーム数だけ requestAnimationFrame を繰り返し呼び出し、指定回数に達したら待機を終了する」ようにします。
  3. これも Promise と async/await を使って待機を実現します。

コード例

function sleep(frames) {
  return new Promise(resolve => {
    let count = 0;
    function animate() {
      // フレーム数をカウント
      count++;
      if (count >= frames) {
        // 指定フレーム数に達したら待機終了
        resolve();
      } else {
        // まだなら次の描画タイミングで再度animateを呼ぶ
        requestAnimationFrame(animate);
      }
    }
    // 最初の呼び出しを開始
    requestAnimationFrame(animate);
  });
}

// 使用例
async function myFunction() {
  console.log("処理開始");
  // 60フレーム待つ (1秒間に約60フレームなので、およそ1秒待つことになる)
  await sleep(60);
  console.log("処理再開");
}

myFunction();

メリット

  • スムーズなアニメーション:ブラウザの描画サイクルと同期するため、アニメーションの途中で待機させたい場合などに、カクつきを防ぎながら自然に処理を一時停止できます。
  • 他の処理を邪魔しない(非同期):setTimeout と同様です。

デメリット

  • 正確な時間制御は難しい:待機時間はフレームレート(PCの性能や負荷によって変動する)に依存します。「正確に2秒待つ」といった制御は苦手です。あくまで「〇フレーム分待つ」という指定になります。
  • ミリ秒指定ではない:フレーム数で指定するため、直感的でない場合があります。

使いどころ

  • Webアニメーションの制御:アニメーションの特定の段階で少し待機を入れたい場合など、描画との連携が重要な場面で役立ちます。
  • 時間精度がそれほど重要でなく、描画を妨げたくない場合に選択肢となります。

5. ジェネレーター関数と yield を使う方法

これは少し高度なテクニックです。関数の実行を途中で止めたり再開したりできる「ジェネレーター関数」という仕組みを使います。

仕組み:

  1. function* で始まる関数が「ジェネレーター関数」です。
  2. 関数内で yield を使うと、そこで処理を一時停止できます。
  3. 別の場所からジェネレーターの .next() メソッドを呼ぶと、停止したところから処理を再開できます。
  4. これと setTimeout(..., 0)(できるだけ早く非同期で処理を実行するおまじない)を組み合わせて、指定時間待つSLEEP機能を実現します。

コード例

// 時間経過をチェックするジェネレーター関数
function* sleepGenerator(ms) {
  const startTime = Date.now();
  while (Date.now() - startTime < ms) {
    // 処理を一時停止し、制御を外部に返す
    yield;
  }
}

// ジェネレーターを使って待機するsleep関数
async function sleep(ms) {
  const generator = sleepGenerator(ms);
  // ジェネレーターが終わる (doneがtrueになる) までループ
  while (!generator.next().done) {
    // イベントループに一旦処理を渡し、他の処理を可能にする
    await new Promise(resolve => setTimeout(resolve, 0));
  }
}

// 使用例 (async/awaitは同じ)
async function myFunction() {
  console.log("処理開始");
  await sleep(2000); // 2秒待つ
  console.log("処理再開");
}

myFunction();

メリット

  • 複雑な非同期処理を分かりやすく書ける可能性:yield を使うことで、複数の非同期ステップを含む処理を、あたかも上から順に実行されるかのように書ける場合があります。
  • 他の処理を邪魔しない(非同期):適切に実装すれば非同期になります。

デメリット

  • コードが複雑になりがち:ジェネレーターとそれを制御する仕組みの両方を理解する必要があり、初心者には難解です。
  • 古い環境では使えない:ジェネレーター関数は比較的最近のJavaScript機能です。
  • async/await で十分な場合が多い:async/await を使えば同様のことがよりシンプルに書ける場合がほとんどです。

使いどころ

  • 非常に複雑な非同期処理のフローを、特定の方法で管理したい場合。
  • 学習目的でジェネレーターの動作を理解するため。
  • 通常のSLEEP機能としては、setTimeout + async/await の方がシンプルです。

6. SharedArrayBuffer と Atomics.wait を使う方法

これは非常に高度で、特殊な状況で使われる方法です。Web Workers(バックグラウンドで別のJavaScriptを実行する仕組み)など、マルチスレッド環境を前提としています。

仕組み

  1. SharedArrayBuffer は、メインのプログラムとWeb Workerなどの間で共有できる特殊なメモリ領域です。
  2. Atomics.wait は、その共有メモリの特定の値が指定した値である間、本当にプログラムの実行を停止(ブロック)させます。他のスレッド(例えばWeb Workerや、setTimeoutで遅延実行される処理)が値を変更すると、停止が解除されます。
  3. setTimeout を使って、指定時間後に共有メモリの値を変更するように仕込み、Atomics.wait でその変更を待つことでSLEEPを実現します。

コード例(注意:そのままでは動かない可能性あり)

// 注意: この機能を使うには、サーバーが特定のHTTPヘッダー(COOP, COEP)を
//       送信するように設定する必要がある場合があります。
//       また、メインスレッドでAtomics.waitを使うとUIが固まります。
//       通常はWeb Worker内で使用します。

async function sleep(ms) {
  // 共有メモリを作成
  const sab = new SharedArrayBuffer(4); // 4バイトの領域
  const int32 = new Int32Array(sab); // 32ビット整数としてアクセス

  // 指定時間後に共有メモリの値を 0 から 1 に変更するタイマーを設定
  // (実際には別スレッドから変更することが多い)
  setTimeout(() => {
    console.log("時間経過、値を変更して起こします");
    Atomics.store(int32, 0, 1); // 値を1に書き込む
    Atomics.notify(int32, 0, 1); // 待っているスレッドに通知 (1つだけ起こす)
  }, ms);

  console.log("Atomics.wait で待機開始 (値が0の間待つ)");
  // 共有メモリのインデックス0の値が 0 である限り待機する
  Atomics.wait(int32, 0, 0); // 第3引数は期待する値
  console.log("待機終了!");
}

// 使用例 (メインスレッドで実行すると固まる可能性が高い)
async function myFunction() {
  console.log("処理開始");
  await sleep(2000); // 2秒待つ (推奨されない使い方)
  console.log("処理再開");
}

// この例はデモのためであり、実際の使用法とは異なる場合があります
// myFunction();

メリット

  • 高精度な待機:CPUに負荷をかけずに、比較的正確な時間、実行をブロックできます(ただしメインスレッド以外で)。
  • スレッド間の同期:Web Workerなどで、他のスレッドからの明確な通知があるまで処理を止めたい場合に有効です。

デメリット

  • 非常に高度で複雑:SharedArrayBuffer, Atomics, マルチスレッドの概念を理解する必要があります。
  • 利用制限:セキュリティ上の理由から、使用するにはサーバー側で特定のHTTPヘッダー(COOP, COEP)を設定する必要があります。
  • メインスレッドでの使用は厳禁:メインスレッド(通常のWebページのJavaScriptが動く場所)で Atomics.wait を使うと、Date.now() のループと同様に UIが完全にフリーズします。Web Worker内での使用が基本です。
  • 初心者向けではない:通常のWeb開発で必要になることは稀です。

使いどころ

  • Web Workers 環境でのスレッド間同期:特定の計算が終わるのを待つ、他のWorkerからのデータ受信を待つなど、バックグラウンド処理同士の連携が必要な場合。
  • 通常のWebページのSLEEP機能としては全く適していません。

まとめと比較

スクロールできます
方法メリットデメリット使いどころ・推奨度
setTimeout + Promise他の処理を止めない (非同期), コードが比較的読みやすいasync/await の理解が必要◎ 最も推奨。ほとんどの状況でこれを使うべき。
Date.now() ループasync/await不要(に見える)CPUを占有し、UIが固まる (同期), 効率が悪い× 非推奨。学習目的以外では絶対に使わない。
setInterval + Promise他の処理を止めない (非同期)clearInterval が必要, 一度きりの待機には setTimeout が自然△ setTimeout で十分なことが多い。特殊な繰り返しチェック待機には使えるかも。
requestAnimationFrameアニメーションと同期, 他の処理を止めない (非同期)正確な時間制御が困難 (フレーム依存), ミリ秒指定ではない○ アニメーション制御や、描画を妨げずに待ちたい場合。時間精度が重要でないなら選択肢。
ジェネレーター + yield複雑な非同期フローを管理しやすい場合がある, 非同期コードが複雑になりがち, ジェネレーターの理解が必要, 古い環境で不可△ 複雑な非同期制御をしたい場合。通常は async/await で代替可能。
SharedArrayBuffer + Atomics高精度な待機 (Worker内), スレッド間同期に有効非常に高度・複雑, 利用制限あり, メインスレッドで使うとUIが固まる× 初心者向けではない。Web Workersでの高度な同期処理が必要な特殊なケースのみ。通常のSLEEPには絶対に使わない。

結論として、JavaScriptで処理を一定時間待たせたい場合は、基本的に setTimeout と Promise、そして async/await を組み合わせる方法を使いましょう。 これが最も安全で、効率的で、現代的な書き方です。 Date.now() を使ったループは絶対に避け、その他の方法は特定の目的に合わせて検討する、というスタンスが良いでしょう。