前回で、async/awaitを使って外部のデータを取得し、件数を選んで表示できるようになった。
ただし、通信に失敗すると画面が「読み込み中…」のまま止まってしまう。シリーズ最終回の今回は、try/catch/finallyとresponse.okを使って、失敗したときの備えを加えよう。
1. 通信の失敗は2種類ある
外部のデータを取得するときの失敗は、大きく2つに分けられる。
- 通信そのものができない:ネットワークの切断など。
fetch()が失敗する - 通信はできたが、エラーが返ってくる:URLの誤りで「404」、サーバーの不具合で「500」など
注意したいのは後者である。fetch()は、404や500が返ってきても、通信自体ができていれば失敗扱いにしない。そのため、応答の状態を自分で確認する必要がある。
2. try/catch/finallyの書き方
エラーが起きるかもしれない処理は、tryの中に書く。
try {
// 失敗するかもしれない処理
} catch (error) {
// 失敗したときの処理
} finally {
// 成功しても失敗しても、最後に必ず行う処理
}tryの中で問題が起きると、残りの処理は飛ばされてcatchに移る。errorには、失敗の内容が入っている。
3. script.js(完成版)
index.htmlは前回のままでよい。script.jsを、次の完成版に置き換えよう。
const BASE_URL = 'https://jsonplaceholder.typicode.com/todos';
const list = document.getElementById('todo-list');
const message = document.getElementById('message');
const select = document.getElementById('limit-select');
const button = document.getElementById('reload-button');
async function loadTodos() {
message.textContent = '読み込み中...';
list.innerHTML = '';
button.disabled = true;
select.disabled = true;
try {
const response = await fetch(`${BASE_URL}?_limit=${select.value}`);
if (!response.ok) {
throw new Error(`HTTPエラー:${response.status}`);
}
const todos = await response.json();
todos.forEach((todo) => {
const li = document.createElement('li');
li.textContent = (todo.completed ? '✅ ' : '⬜ ') + todo.title;
list.appendChild(li);
});
message.textContent = `${todos.length}件取得した`;
} catch (error) {
message.textContent = `取得に失敗した(${error.message})`;
} finally {
button.disabled = false;
select.disabled = false;
}
}
select.addEventListener('change', loadTodos);
button.addEventListener('click', loadTodos);
loadTodos();4. コードの解説
前回のコードとの違いは、取得処理をtryで囲んだことと、response.okの確認を加えたことである。
response.okは、応答が成功(200番台)のときにtrueになる。falseなら、throw new Error()でわざとエラーを発生させ、catchに処理を移している。response.statusはHTTPのステータスコードで、成功なら200など、見つからなければ404である。
ネットワークの切断のようにfetch()自体が失敗した場合も、同じcatchで受け止められる。2種類の失敗を、1か所でまとめて扱えるわけである。
catchでは、error.messageを画面に表示している。ユーザーに「何が起きたのか」が伝わるため、画面が止まったように見えることがない。
finallyには、ボタンと選択欄を有効に戻す処理を置いた。成功しても失敗しても必ず実行されるので、操作できないままになることがない。前回は、この処理が失敗時に実行されなかったことが問題だったのである。
5. わざと失敗させてみよう
エラー処理は、実際に失敗させないと動作を確認できない。次の2つを試してみよう。
(画像は筆者のChromeで試したもの)
1.script.jsのBASE_URLの「todos」を「todoss」に書き換えて、再度index.htmlをダブルクリックしてブラウザを起動しよう。

「取得に失敗した(HTTPエラー:404)」と表示される。

2.BASE_URLを元のtodosに戻して保存し、F5キーで再読み込みする。
次にF12キーで開発者ツールを開き、「ネットワーク」タブで「キャッシュを無効化」にチェックを入れ、通信状態のプルダウン(「スロットリングなし」などと表示されている)から「オフライン」を選ぶ。

開発者ツールを開いたまま、画面の「再読み込み」ボタンを押す。

ブラウザによって文言は異なるが、「取得に失敗した(Failed to fetch)」などと表示される。

どちらの場合も画面は止まらず、ボタンも選択欄も操作できる状態に戻る。
確認が済んだら、元に戻しておこう。
6. シリーズのまとめ
3回にわたって、外部データを取得して画面に表示する流れを扱ってきた。
fetch()でデータを取得し、response.json()で変換する(第15回)async/awaitで非同期処理を順番どおりに書く(第16回)try/catch/finallyとresponse.okで失敗に備える(第17回)
次のステップとしては、取得したタスクをこれまでのToDoリストに取り込み、localStorageに保存するといった発展が考えられる。ぜひ自分なりに組み合わせてみてほしい。


コメント