JavaScript Tips | 基本・共通ユーティリティ:汎用 – タイムアウト実行

JavaScript JavaScript
スポンサーリンク

タイムアウト実行とは何を守るための仕組みなのか

「タイムアウト実行」は、
“一定時間以内に終わらなかった処理を強制的に中断し、失敗として扱う”
ためのユーティリティです。

業務システムでは、次のような問題が頻繁に起きます。

  • 外部 API が返ってこない
  • ネットワークが不安定で応答が遅い
  • 重い処理が予想以上に時間を食ってしまう
  • いつまでも待ち続けると画面が固まる

こうした「終わらない処理」を放置すると、ユーザー体験もシステムの安定性も大きく損なわれます。

そこで必要になるのが タイムアウト実行
「この処理は最大 3 秒だけ待つ。それ以上かかったら強制的に失敗扱いにする」
という安全装置をつけるイメージです。


Promise をタイムアウト付きで実行する基本ユーティリティ

まずは最もシンプルな形を見てみる

function withTimeout(promise, ms) {
  return new Promise((resolve, reject) => {
    const timer = setTimeout(() => {
      reject(new Error(`Timeout after ${ms}ms`));
    }, ms);

    promise
      .then((value) => {
        clearTimeout(timer);
        resolve(value);
      })
      .catch((err) => {
        clearTimeout(timer);
        reject(err);
      });
  });
}
JavaScript

この withTimeout は、
「指定した時間以内に終わらなければ reject する Promise」
を返す関数です。

動きはこうです。

  1. setTimeout で「ms ミリ秒後に強制エラー」を予約する
  2. 本来の Promise が先に成功したら、タイマーを解除して resolve
  3. 本来の Promise が先に失敗したら、タイマーを解除して reject
  4. どちらも起きず ms が経過したら、タイムアウトエラーを投げる

これがタイムアウト実行の基本構造です。


実際に API 呼び出しに使ってみる

例えば、ユーザー情報を取得する API があるとします。

async function fetchUser() {
  const res = await fetch("/api/user");
  return res.json();
}
JavaScript

これをタイムアウト付きで呼ぶにはこうします。

async function loadUser() {
  const user = await withTimeout(fetchUser(), 3000);
  console.log("取得成功:", user);
}
JavaScript

このコードの意味は、

  • fetchUser() が 3 秒以内に終われば成功
  • 3 秒経っても返ってこなければ Timeout after 3000ms で失敗

という動きになります。

業務では「API が遅いときに永遠に待つ」ことが最も危険なので、
このように「最大待ち時間」を決めるのは非常に重要です。


async 関数を直接タイムアウト付きで実行する版

Promise を渡すのではなく「関数」を渡したい場合

次のようなユーティリティもよく使われます。

async function runWithTimeout(fn, ms) {
  return withTimeout(fn(), ms);
}
JavaScript

使い方はこうです。

const result = await runWithTimeout(() => fetch("/api/data"), 2000);
JavaScript

この形のメリットは、
「関数を渡すだけでタイムアウト付き実行ができる」
という点です。


タイムアウト実行の重要ポイント:中断できるわけではない

ここで初心者がよく誤解するポイントがあります。

タイムアウト実行は「処理を止める」わけではありません。
“待つのをやめる”だけです。

例えば、fetch は途中で中断されません。
内部ではまだ通信が続いている可能性があります。

本当に中断したい場合は、AbortController を使います。


AbortController を使った「本当に中断する」タイムアウト実行

fetch をタイムアウトで中断する実務的な書き方

async function fetchWithTimeout(url, ms) {
  const controller = new AbortController();
  const timer = setTimeout(() => controller.abort(), ms);

  try {
    const res = await fetch(url, { signal: controller.signal });
    return res;
  } finally {
    clearTimeout(timer);
  }
}
JavaScript

使い方はこうです。

try {
  const res = await fetchWithTimeout("/api/user", 3000);
  const data = await res.json();
  console.log("成功:", data);
} catch (err) {
  console.error("タイムアウトまたは中断:", err.message);
}
JavaScript

ここでの深掘りポイントは、

  • AbortController が fetch を本当に中断してくれる
  • タイムアウト後は通信自体が止まる
  • サーバー側の負荷も減らせる

という点です。

業務では「中断できるタイムアウト」が必要な場面が多いので、
このパターンは非常に実用的です。


タイムアウト実行と再試行処理の組み合わせ

実務では、タイムアウトと再試行を組み合わせることが多いです。

例えば、

  • 3 秒以内に返ってこなければタイムアウト
  • 最大 3 回まで再試行
  • それでもダメならエラー

というような制御です。

async function fetchUserSafely() {
  return retry(
    () => fetchWithTimeout("/api/user", 3000),
    { maxAttempts: 3, delayMs: 1000 }
  );
}
JavaScript

これで、

  • 遅い API は 3 秒で切る
  • 一時的な障害なら再試行で回復
  • 永遠に待つこともない

という「しぶとくて安全な処理」が実現できます。


実務での具体的な利用イメージ

ローディング画面の制御

「API が遅すぎるときはローディングを消してエラー表示に切り替える」
という UI 制御はよくあります。

async function loadData() {
  try {
    const data = await withTimeout(fetch("/api/data"), 5000);
    render(data);
  } catch {
    showError("通信がタイムアウトしました");
  }
}
JavaScript

ユーザー体験を守るためにも、タイムアウトは必須です。

バッチ処理の安全装置

バッチ処理で「一つのタスクが永遠に終わらない」問題は致命的です。

async function runTaskSafely(task) {
  return runWithTimeout(task, 10000); // 10秒以内に終わらなければ失敗扱い
}
JavaScript

これにより、バッチ全体が止まる事故を防げます。


小さな練習で感覚をつかむ

次のような「わざと遅い処理」を作って、
タイムアウト実行を試してみてください。

function slowTask() {
  return new Promise((resolve) => {
    setTimeout(() => resolve("完了"), 5000);
  });
}

async function test() {
  try {
    const result = await withTimeout(slowTask(), 2000);
    console.log(result);
  } catch (err) {
    console.log("タイムアウト:", err.message);
  }
}

test();
JavaScript

「5 秒かかる処理を 2 秒で打ち切る」という動きを体感すると、
タイムアウト実行の重要性がよく分かります。


必要であれば、
「中断できるタイムアウト」
「タイムアウト+再試行」
「タイムアウト付きの並列処理」
など、さらに実務寄りの応用パターンも解説できます。

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