【第8回】「データが取れません」の原因を自分で言えるようになる|async/await(嫁子が教えるTS講座)
今回のゴール
- なぜ「待つ」仕組みが必要なのかを説明できる
async/awaitがついたコードを読めるawaitを付け忘れると何が起きるかが分かる
前回(関数)の続きです。今回は第2部でいちばん難しいところですが、Webで何か作るなら必ず出てきます。全体の流れは講座の目次へ。
嫁子: 先に言っておくね。今日の回は、一度読んで分からなくても正常だよ。
私も「非同期」という言葉自体が良くないと思ってる。難しそうに聞こえるでしょ。やってることは「時間のかかる作業の返事を待つ」だけなの。
完全に理解しなくていいから、
awaitという単語を見たら「ここで待ってるんだな」と分かるところまで来てね。それだけで、この先のエラーの半分は読めるようになるよ。
① なぜ「待つ」仕組みが要るのか
プログラムは基本的に上から順に実行されます。ところが、次のような処理はすぐには終わりません。
- サーバーからデータを取ってくる
- 画像を読み込む
- ファイルを保存する
どれも相手の返事を待つ必要があり、速くても0.1秒、遅ければ数秒かかります。
このとき、返事を待つあいだ画面全体を止めてしまうと、その数秒はボタンを押しても何も反応しなくなります。それを避けるため、JavaScriptは「時間のかかる処理は後回しにして、先に進む」という仕組みを持っています。
これが非同期処理です。
嫁子: 家事でたとえるとね。洗濯機を回してる3分のあいだ、洗濯機の前で突っ立ってる人はいないでしょ。
その間に別のことをして、終わったら取りに行く。JavaScriptも同じで、待ってる間に他の仕事をするの。今日はそのための書き方を覚える回だよ。
② Promise — 「あとで結果を渡す」という約束
時間のかかる処理は、その場では結果を返せません。代わりに「終わったら結果を渡します」という引換券を返します。これがPromiseです。
const promise = fetch("https://example.com/data.json");
console.log(promise); // Promise { <pending> }
fetch はサーバーからデータを取ってくる命令です。この時点ではまだデータは届いておらず、pending(処理中)と表示されます。
引換券のままでは中身は使えません。受け取るには次の書き方を使います。
③ async / await — 待ってから受け取る
async function getData() {
const res = await fetch("https://example.com/data.json");
const data = await res.json();
console.log(data);
}
読み方はこうです。
await— 「ここで結果が来るまで待つ」async— 「この関数の中ではawaitを使います」という宣言
await は async のついた関数の中でしか使えません。この2つはセットで出てきます。
上の例では、まずサーバーの返事を待ち、次にその返事を扱える形(JSON)に変換するのをまた待っています。await が2回あるのは、待つ場面が2回あるからです。
嫁子:
asyncとawaitがセットなのは、単純に「宣言してから使う」ってだけなの。深い意味を探さなくていいよ。それより大事なのは、
awaitが付いている行は、そこで一旦止まるということ。この感覚があると、コードを追いかけるとき迷わなくなるよ。
④ await を付け忘れるとどうなるか
ここが今日いちばん実用的なところです。
async function getData() {
const res = fetch("https://example.com/data.json"); // await を忘れた
console.log(res); // Promise { <pending> }
}
エラーは出ません。でも res には引換券が入ったままなので、その先で中身を取り出そうとすると undefined になります。
「データが取れない」「なぜか空になる」の原因は、かなりの割合でこれです。
| 症状 | 疑うところ |
|---|---|
Promise { <pending> } と表示される |
await の付け忘れ |
データが undefined |
await の付け忘れ、または名前の打ち間違い |
嫁子: これ、AIが書いたコードでも起きるよ。特に長いコードを一気に書かせたときにね。
だから「データが空です」とだけ伝えるんじゃなくて、「
resがPromise { pending }になっています」と言えると、AIは一発で直せるの。第1回で言った「どこが悪いか伝えられる」って、こういうことだよ。
⑤ 失敗したときの処理
ネットワークは失敗します。サーバーが落ちていることも、電波が切れることもあります。その場合に備える書き方が try / catch です。
async function getData() {
try {
const res = await fetch("https://example.com/data.json");
const data = await res.json();
console.log(data);
} catch (error) {
console.log("取得に失敗しました", error);
}
}
try の中でエラーが起きたら、その時点で catch に飛びます。失敗しても、プログラム全体は止まりません。
嫁子: AIに何か作らせるとき、「エラー処理も入れて」と一言足すといいよ。
言わないと、うまくいく場合だけのコードが返ってくることがあるの。動くから気づかないんだけど、通信が切れた瞬間に画面が真っ白になったりする。動く道だけを作ってあるコードは、まだ半分だと思っておいてね。
つまずきポイント
await はどこにでも書けるわけではありません。async のついた関数の中だけです。外で使うとエラーになります。
.then() という書き方も見かけます。これはPromiseの古い書き方で、意味は await とほぼ同じです。読めれば十分で、自分で書く必要はありません。
fetch("...").then((res) => res.json()).then((data) => console.log(data));
嫁子:
.thenがつながってるコード、正直読みにくいよね。だからasync/awaitが主流になったの。古い記事にはまだ出てくるから、「同じことを別の書き方でやってる」と思って読み飛ばして大丈夫だよ。
今回のまとめ
- 時間のかかる処理は、待つあいだ画面を止めないために非同期で実行される
- Promiseは「あとで結果を渡す」引換券。そのままでは中身を使えない
awaitで待って受け取る。awaitはasync関数の中でだけ使えるawaitの付け忘れはPromise { <pending> }やundefinedの原因- 通信は失敗するので、
try/catchで備える
次にやること
次回はブラウザで動かすです。ここまでのコードは実行しても文字が出るだけでしたが、次回から画面を書き換える話になります。
今回の宿題:await を付けた場合と外した場合で、console.log の結果がどう変わるか見てください。Promise { <pending> } という表示を一度見ておくと、実際に遭遇したとき「あ、これか」となります。
嫁子: 今日の内容が半分しか分からなくても、先に進んで大丈夫だよ。
非同期は、実際に何か作りながらのほうが理解が早いの。第5部でサーバーからデータを取るとき、もう一度ここに戻ってくると、たぶんすんなり入るからね。