async/await復習
待つ必要があるもの、ないもの
前のレッスンで await fs.readFile(...) と書きました。なぜ await が要るのかを、ここで整理しておきます。
プログラムには 2 種類の処理があります。
| 種類 | 例 | 待ち方 |
|---|---|---|
| すぐ終わる | 足し算、配列の絞り込み | 待たなくてよい |
| 時間がかかる | ファイルの読み書き、通信 | 結果を待つ必要がある |
時間がかかる処理は、その場で答えを返せません。代わりに「あとで結果を渡します」という引換券を返します。これが Promise です。
await は引換券を結果に換える
JavaScript
const raw = await fs.readFile("items.json", "utf8");await は「引換券のままではなく、中身が届くまで待つ」という指示です。付け忘れると、変数には引換券そのものが入ります。
JavaScript
const raw = fs.readFile("items.json", "utf8"); // await 忘れ
JSON.parse(raw); // 中身ではなく引換券を渡しているこれはエラーになりますが、種類によってはエラーにならずに変な値のまま進むこともあります。実践JavaScript で fetch に await を付け忘れたときと同じ失敗です。
await は async の中でしか書けない
await を使う関数には async を付けます。
JavaScript
async function loadItems(filePath) {
const fs = require("fs/promises");
const raw = await fs.readFile(filePath, "utf8");
return JSON.parse(raw);
}そして async を付けた関数は、必ず Promise を返します。ですから呼ぶ側も await が要ります。この連鎖は、待つ必要のあるものに触れた時点から、呼び出しの流れをさかのぼって続きます。
順に待つか、まとめて待つか
複数のファイルを読むとき、書き方で速さが変わります。
JavaScript
// 順に待つ 1 つ目が終わってから 2 つ目を始める
const a = await fs.readFile("a.json", "utf8");
const b = await fs.readFile("b.json", "utf8");
// まとめて待つ 2 つを同時に始めて、両方揃うのを待つ
const [a2, b2] = await Promise.all([
fs.readFile("a.json", "utf8"),
fs.readFile("b.json", "utf8"),
]);互いに関係のない処理なら、Promise.all のほうが待ち時間は短くなります。前の結果を使って次を決めるなら、順に待つしかありません。
手を動かす
演習では、ファイルを読み込んでから加工して返すところまでを、非同期でつなぎます。await を置く場所を、自分で決めてください。
要件
- 受け取った配列を JSON の文字列にして、受け取った場所へ書き込む
- 同じファイルを
utf8で読み直し、JSON として解釈する soldがtrueでない商品のnameだけを、元の並び順のまま配列で返す
入出力例
loadAvailableNames([{"id":1,"name":"レザースニーカー","sold":false},{"id":2,"name":"木製のスツール","sold":true},{"id":3,"name":"古着のデニムジャケット","sold":false}], "/tmp/fleama-async-a.json") → ["レザースニーカー","古着のデニムジャケット"]
loadAvailableNames([{"id":1,"name":"木製のスツール","sold":true}], "/tmp/fleama-async-b.json") → []
loadAvailableNames([], "/tmp/fleama-async-c.json") → []ヒント
編集 ゆめさく編集部