「再試行処理」を一言でいうと
fetch の「再試行処理」は、
「1 回の通信が失敗したときに、すぐ諦めずにもう何回かやり直す仕組み」 のことです。
ネットワークの世界では、
「たまたま一瞬だけ回線が不安定だった」
「サーバーが一瞬重かった」
といった“たまたまの失敗”がよくあります。
そんなときに、
「1 回失敗した=即エラー表示」ではなく、
「何回かリトライして、それでもダメならエラーにする」
というのが「再試行処理」です。
ここが重要です。
再試行処理は 「全部 retry すれば偉い」わけではありません。
どのエラーなら再試行して意味があるのか(ネットワーク一時的トラブルなど)、
どのエラーは再試行しても意味がないのか(パラメータが不正など)、
それを見分けた上で「上手に諦める」ための仕組みです。
まず前提:どんな失敗なら「再試行する意味がある」のか
再試行で改善しやすいエラー
ざっくりいうと、こんなタイプのエラーは再試行に向いています。
一時的なネットワークエラー
例: Wi-Fi が一瞬途切れた、回線が一瞬だけ不安定だった。
サーバーの一時的な負荷・不調
例: 503 Service Unavailable(今ちょっと無理)、
あるいはたまたま一瞬 500 が返ってきたが、すぐ復活しそうな状況。
こういうのは「時間を少し置いてもう一度叩けば成功する」可能性が高いので、
再試行が効果的です。
再試行しても無駄になりやすいエラー
逆に、何度やっても改善しないタイプのエラーもあります。
リクエスト内容が明らかに不正(400 Bad Request)
例: 必須パラメータが抜けている、型が違うなど。
認証・権限の問題(401, 403)
例: ログインしていない、トークンが失効している。
URL が間違っている 404 Not Found
例: エンドポイント自体が存在しない。
これらは 「リクエストをどう直すか」の問題 であって、
「回数を増やせば解決する」話ではありません。
再試行するより、早くユーザーや開発者に「内容がおかしい」と伝えるべきケースです。
ここが重要です。
再試行処理を設計するときは、
「どのエラーなら“待つ+やり直す”価値があるのか?」
「どのエラーは即座に諦めてユーザーに知らせるべきか?」
を切り分けることから始めると、無意味な retry 地獄を防げます。
一番シンプルな「固定回数リトライ」の形
基本アイデア
まずは「最大 3 回まで試して、全部ダメならエラー」という、固定回数リトライのテンプレートから。
雰囲気だけ先に見ると、こうです。
async function fetchWithRetry(url, options = {}, maxRetries = 3) {
let lastError;
for (let attempt = 1; attempt <= maxRetries; attempt++) {
try {
const response = await fetch(url, options);
if (!response.ok) {
throw new Error("HTTPエラー: " + response.status);
}
return response; // 成功したらここで終了
} catch (error) {
lastError = error;
console.warn(`試行 ${attempt} 回目で失敗:`, error.message);
if (attempt === maxRetries) {
break;
}
}
}
throw lastError;
}
JavaScriptコードを丁寧に分解する
for (let attempt = 1; attempt <= maxRetries; attempt++) { ... }
ここで「1 回目、2 回目、…」と試行回数を回しています。
const response = await fetch(url, options);
普通に fetch を呼ぶ。ネットワークエラーならここで throw されて catch に飛びます。
if (!response.ok) { throw new Error("HTTPエラー: " + response.status); }
404 や 500 など HTTP エラーも、自分で Error に変換して throw。
これで「fetch 自体の失敗」と「HTTP ステータスの失敗」を同じレイヤーで扱えるようになります。
return response;
どこかの試行で成功したら、その場で return して関数を終了。
その後の試行は行われません。
catch (error) { ... }
どこかで失敗した場合、ここでログを出しつつ、attempt === maxRetries かどうかを見ます。
まだ試行回数が残っているなら、何もせずループを続行。
最大回数に達していたら break して throw lastError へ。
throw lastError;
最後に失敗したエラーを、そのまま呼び出し元に投げ返します。
使い方のイメージ
async function loadData() {
try {
const response = await fetchWithRetry("https://example.com/api/data");
const data = await response.json();
console.log("成功:", data);
} catch (error) {
console.error("最終的に失敗:", error.message);
}
}
JavaScriptここが重要です。
この「固定回数リトライ」は、
「多少の揺らぎには強くなるけれど、無限に retry してしまう危険はない」 という、安全な基本形です。
まずはこのパターンをしっかり理解して、自分で書けるようになることを目標にしてください。
再試行のたびに「少し待つ」:簡単な待機を入れる
連続して叩き続けると逆効果なこともある
固定回数リトライを、そのまま連続で叩き続けると、
「サーバーが一瞬重い」
「回線が一瞬不安定」
のタイミングで、
同じような失敗を高速で 3 回連続させてしまうことがあります。
それを避けるために、
「失敗したら、少し待ってから次の試行をする」 という待機を入れることがあります。
簡単な「一定時間待つ」版
function delay(ms) {
return new Promise((resolve) => setTimeout(resolve, ms));
}
async function fetchWithRetry(url, options = {}, maxRetries = 3, delayMs = 500) {
let lastError;
for (let attempt = 1; attempt <= maxRetries; attempt++) {
try {
const response = await fetch(url, options);
if (!response.ok) {
throw new Error("HTTPエラー: " + response.status);
}
return response;
} catch (error) {
lastError = error;
console.warn(`試行 ${attempt} 回目で失敗:`, error.message);
if (attempt === maxRetries) {
break;
}
await delay(delayMs); // ここで少し待つ
}
}
throw lastError;
}
JavaScriptここでは、
delay(ms) で「ms ミリ秒後に resolve される Promise」を作り、await delay(delayMs) で実際に待機しています。
ここが重要です。
待機を入れるだけで、
「サーバーに連打しない」「一時的な揺らぎが収まる時間を与える」 という意味が生まれます。
ただ retry 回数を増やすより、「間隔を空ける」という発想もセットで持っておくと、より現実的な設計になります。
エラーの種類を見て「再試行するかどうか」を分ける
「何でもかんでも retry」しないためのフィルタ
さっきの実装だと、
HTTP ステータスが 400 や 401 のときも retry してしまいます。
これは現実的にはあまり嬉しくありません。
そこで、「再試行したいエラーだけ retry する」ように条件を足します。
例として、
- ネットワークエラー(fetch が throw)
- 500番台(サーバー側エラー)
のときだけ再試行し、
他(400番台など)は即座に失敗とみなす、という形にしてみます。
条件付きリトライのコード例
function shouldRetry(error, response) {
if (error) {
return true;
}
if (!response) {
return false;
}
if (response.status >= 500 && response.status < 600) {
return true;
}
return false;
}
async function fetchWithRetry(url, options = {}, maxRetries = 3, delayMs = 500) {
let lastError;
for (let attempt = 1; attempt <= maxRetries; attempt++) {
let response = null;
let error = null;
try {
response = await fetch(url, options);
if (!response.ok) {
error = new Error("HTTPエラー: " + response.status);
} else {
return response;
}
} catch (err) {
error = err;
}
lastError = error;
console.warn(`試行 ${attempt} 回目で失敗:`, error.message);
if (attempt === maxRetries || !shouldRetry(error, response)) {
break;
}
await delay(delayMs);
}
throw lastError;
}
JavaScriptここでのポイントは shouldRetry 関数です。
error がある(=ネットワークエラーなど)なら true。response.status が 500〜599 なら true(サーバー側エラーとして再試行対象)。
それ以外(400番台など)のときは false を返して、
「このエラーは再試行しても意味がなさそうだから、ここで諦める」という判断をします。
ここが重要です。
再試行処理の質は、「何回やるか」ではなく、「どのエラーを retry 対象にするか」で決まる と言ってもいいです。
shouldRetry の中身を API や仕様に合わせて調整できるようになると、一気に“本番っぽい”コードになります。
再試行処理と UI の関係(ユーザーにどう見えるか)
ユーザーから見た「無限に待たされている感」をなくす
再試行処理を入れると、
「1 回の fetch で 3 回分の時間を使う」可能性があります。
例えば 1 回あたり 3 秒のタイムアウト × 3 回リトライ → 最大 9 秒。
その間ずっと「読み込み中」だけ出ていたら、ユーザーは不安になります。
そこで、UI 側ではこんな配慮が必要になります。
ローディング表示を出しつつ、「再試行中…」などの文言に変える。
一定回数失敗したら、「通信が不安定です。ネットワークを確認してから再度お試しください。」と明示する。
「再読み込み」ボタンを出して、ユーザーに主導権を返す。
実装イメージ(擬似コード)はこんな感じです。
async function loadWithRetryAndUI() {
setLoading(true);
setMessage("読み込み中...");
try {
const response = await fetchWithRetry("/api/data", {}, 3, 1000);
const data = await response.json();
showData(data);
setMessage("");
} catch (error) {
setMessage("通信に失敗しました。ネットワークを確認して再度お試しください。");
} finally {
setLoading(false);
}
}
JavaScriptここが重要です。
再試行処理は、コードだけの話ではありません。
「その間ユーザーに何が見えて、何秒くらい待たされるのか」をセットで考える必要があります。
待たせるなら、何回目の試行なのか、どこまで頑張るのかを、テキストや UI で伝えると親切です。
初心者として「再試行処理」で本当に押さえてほしいこと
再試行処理は、「一度の失敗で諦めず、決めたルールの範囲でやり直す仕組み」。
単に回数を増やすのではなく、「どの種類の失敗なら retry に意味があるか」を考えるのが大事。
基本の形は「固定回数リトライ」。for で試行回数を回し、try/catch の中で fetch → ok チェック → 成功なら return → 失敗なら次のループ、という流れを書く。
ネットワークエラーや 500番台のサーバーエラーなど、「一時的な問題」に対してだけ再試行する。
400番台や認証エラーなど、内容が間違っている場合は即座に諦める。
連続で叩き続けるのではなく、失敗ごとに await delay(ms) で少し待ってから次の試行をする。
サーバーやネットワークに「回復する時間」を与えるつもりで。
ユーザーの体験もセットで考える。
再試行中に何が表示されるか、最大で何秒待たせるか、
最終的に失敗したらどんなメッセージと「次の行動(再読み込みなど)」を提示するかを設計する。
ここが重要です。
再試行処理は、“ただの根性論” ではありません。
「どこまで頑張るか」「どこで諦めるか」を、自分で決められるようになる技術です。
コードを書くときには、
「これは一時的なエラーか?」
「もう一度やれば成功する可能性があるか?」
「ユーザーは何秒くらいなら許容してくれそうか?」
と自分に問いかけながら retry ロジックを組んでみてください。
その問いを重ねるほど、あなたの非同期処理は“現実に強い”ものになっていきます。
