通信エラーを適切に処理しよう
更新日:2026-08-02
通信は、URLの間違い、サーバー障害、ネットワーク切断などで失敗します。開発者向け情報と利用者向け表示を分けて扱います。
前回はfetch()の基本を学びました。今回は失敗を「HTTPエラー」「ネットワークエラー」「データ形式のエラー」に分け、再試行できる読み込み画面へ改善します。
今回使用するファイル構成
javascript-practice/
├── index.html
└── js/
└── script.js
通信失敗を処理する流れ
response.okを確認する
async function fetchArticles() {
const response = await fetch('/api/articles');
if (!response.ok) {
throw new Error(`HTTP error: ${response.status}`);
}
return response.json();
}
response.okはHTTPステータスが200番台ならtrueです。404や500を成功データとして処理しないよう明示的に確認します。
tryとcatchで失敗を扱う
async function loadArticles() {
try {
const articles = await fetchArticles();
renderArticles(articles);
showStatus('読み込みが完了しました');
} catch (error) {
console.error(error);
showStatus('記事を取得できませんでした。時間をおいてお試しください。');
}
}
Consoleには調査用の詳細、画面には利用者が次に取れる行動を表示します。スタックトレースや内部URLをそのまま画面へ出しません。
finallyで後処理する
try {
loadButton.disabled = true;
await fetchArticles();
} catch (error) {
console.error(error);
} finally {
loadButton.disabled = false;
}
finallyは成功・失敗にかかわらず実行されます。
エラーの種類を区別する
HTTPエラー
サーバーからレスポンスは届いたものの、要求が成功しなかった状態です。代表例は404と500です。
- 400:送った内容に問題がある
- 401:認証が必要
- 403:操作する権限がない
- 404:URLに対応するデータがない
- 500:サーバー内部で問題が発生した
fetch()はレスポンスを受け取れているため、404や500だけではPromiseを拒否しません。response.okを自分で確認します。
ネットワークエラー
オフライン、名前解決の失敗、接続拒否など、レスポンス自体を受け取れない状態です。この場合はfetch()が拒否され、catchへ移ります。
データ形式のエラー
成功レスポンスでも、壊れたJSONや想定外の構造が返る場合があります。response.json()の失敗やArray.isArray()による検査も同じcatchで扱えます。
再試行できる画面を完成させる
<button id="load-button" type="button">記事を読み込む</button>
<button id="retry-button" type="button" hidden>もう一度試す</button>
<p id="status" aria-live="polite"></p>
<ul id="article-list"></ul>
const loadButton = document.querySelector('#load-button');
const retryButton = document.querySelector('#retry-button');
const status = document.querySelector('#status');
const articleList = document.querySelector('#article-list');
async function fetchArticles() {
const response = await fetch('data/articles.json');
if (!response.ok) {
throw new Error(`HTTP ${response.status}`);
}
const articles = await response.json();
if (!Array.isArray(articles)) {
throw new TypeError('記事データが配列ではありません');
}
return articles;
}
function renderArticles(articles) {
articleList.replaceChildren();
articles.forEach((article) => {
const item = document.createElement('li');
item.textContent = article.title;
articleList.append(item);
});
}
async function loadArticles() {
status.textContent = '記事を読み込んでいます。';
loadButton.disabled = true;
retryButton.hidden = true;
try {
const articles = await fetchArticles();
renderArticles(articles);
status.textContent = `${articles.length}件を読み込みました。`;
} catch (error) {
console.error('記事取得に失敗しました', error);
status.textContent = '記事を取得できませんでした。通信環境を確認して、もう一度お試しください。';
retryButton.hidden = false;
} finally {
loadButton.disabled = false;
}
}
loadButton.addEventListener('click', loadArticles);
retryButton.addEventListener('click', loadArticles);
利用者向けメッセージには、内部URLやスタックトレースを表示しません。開発者が調査する詳細はconsole.error()へ残します。
DevToolsで失敗を再現する
正常系だけでなく、意図的に失敗させて確認します。
- URLを
articles-missing.jsonへ変えて404を発生させる - JSONの末尾へ余分なカンマを付けて解析を失敗させる
- DevToolsのNetworkタブをOfflineにして通信を試す
- どの場合もボタンが再び操作可能になることを確認する
エラー確認後は、URL、JSON、ネットワーク設定を元に戻してください。
まとめ
HTTP結果はresponse.ok、例外はtryとcatchで処理します。次の記事ではコードをモジュールへ分割します。