ここまで計3回にわたって[[Promise (JavaScript)|Promise]]を学びました。準備はできましたので、近代の非同期処理である [[async function (JavaScript)|async function]]と[[await (JavaScript)|await]] を学びましょう。 ## Reference - [非同期処理:Promise/Async Function · JavaScript Primer \#jsprimer](https://jsprimer.net/basic/async/) ## Lesson ### 大事な3つのこと [[async function (JavaScript)|async function]]と[[await (JavaScript)|await]]で覚える重要なことは**3つだけ**です。 - [[await (JavaScript)|await]]演算子をあてると[[Promise (JavaScript)|Promise]]が解決するまで待機する - 成功した場合は `resolve(...)` で指定した値が返る - 失敗した場合は `reject(...)` で指定したエラーがthrowされる - [[await (JavaScript)|await]]演算子は以下いずれかの状況でしか使用できない - [[async function (JavaScript)|async function]]の中 - [[モジュール (JavaScript)|モジュール]]の最上位 - [[async function (JavaScript)|async function]]は **必ず** [[Promise (JavaScript)|Promise]]を返す ### Promiseのコードをasync/awaitに変換してみる コードで動作を確認してみましょう。以下のコードをいじっていきます。 ```js function delay(ms) { return new Promise((resolve) => { setTimeout(() => { resolve(`${ms}ms経ちました`); }, ms); }); } function main() { delay(500) .then((msg) => { console.log(msg); return delay(1000); }) .then((msg) => { console.log(msg); return delay(1500); }) .then((msg3) => { console.log(msg3); }); } main(); ``` `main` 関数を [[async function (JavaScript)|async function]] に変換し、中で [[await (JavaScript)|await]] を使ったコードに書き換えます。 ```js async function main() { const msg1 = await delay(500); console.log(msg1); const msg2 = await delay(1000); console.log(msg2); const msg3 = await delay(1500); console.log(msg3); } ``` 変数への代入とログ出力を1行にまとめることで、更に簡略化できます。 ```js async function main() { console.log(await delay(500)); console.log(await delay(1000)); console.log(await delay(1500)); } ``` ### main関数の返却値 先ほど > [[async function (JavaScript)|async function]]は **必ず** [[Promise (JavaScript)|Promise]]を返す と説明ましたが、`main` 関数は[[async function (JavaScript)|async function]]なので[[Promise (JavaScript)|Promise]]を返すようになります。そのため、以下のコードは2つの `main` 関数が同時に並行実行されます。 ```js async function main() { console.log(await delay(500)); console.log(await delay(1000)); console.log(await delay(1500)); } main(); main(); ``` それぞれの `main` 関数を直列に実行させたい場合は、`main` 関数が返却する[[Promise (JavaScript)|Promise]]を[[await (JavaScript)|await]]しましょう。[[await (JavaScript)|await]]演算子は[[モジュール (JavaScript)|モジュール]]の最上位でも利用可能です。 ```js async function main() { console.log(await delay(500)); console.log(await delay(1000)); console.log(await delay(1500)); } await main(); await main(); ``` ## Mission 1 #😁EASY 以下のコードを async/await で書き直してください。 ```js function delayOutput(delaySec, output) { return new Promise((resolve) => { setTimeout(() => { resolve(output); }, delaySec * 1000); }); } delayOutput(3, "hogehoge").then((r) => { console.log(r); }); ``` %% 解答例 ```js function delayOutput(delaySec, output) { return new Promise((resolve) => { setTimeout(() => { resolve(output); }, delaySec * 1000); }); } const r = await delayOutput(3, "hogehoge"); console.log(r); ``` %% ## Mission 2 #🙂NORMAL 以下のコードを async/await で書き直してください。 ```js function createCapRamenPromise() { return new Promise((_, reject) => { setTimeout(() => { reject(new Error("ラーメンはうまくできませんでした")); }, 3 * 1000); }); } createCapRamenPromise() .catch((err) => { console.error(err); }); ``` %% 解答例 ```js function createCapRamenPromise() { return new Promise((_, reject) => { setTimeout(() => { reject(new Error("ラーメンはうまくできませんでした")); }, 3 * 1000); }); } try { await createCapRamenPromise(); } catch (err) { console.error(err); } ``` %% > [!hint]- Hint 1 > [[try...catch文 (JavaScript)|try...catch文]]を使いましょう。 ## Mission 3 #🙂NORMAL 以下のコードを async/await で書き直してください。 ```js function delay(ms) { return new Promise((resolve) => { setTimeout(resolve, ms); }); } console.time("log"); console.timeLog("log", "開始"); delay(1000) .then(() => { console.timeLog("log", "並列処理開始"); return Promise.all([delay(500), delay(1500)]); }) .then(() => { console.timeLog("log", "並列処理終了"); return delay(1000); }) .then(() => { console.timeLog("log", "終了"); }); ``` %% 解答例 ```js function delay(ms) { return new Promise((resolve) => { setTimeout(resolve, ms); }); } console.time("log"); console.timeLog("log", "開始"); await delay(1000); console.timeLog("log", "並列処理開始"); await Promise.all([delay(500), delay(1500)]); console.timeLog("log", "並列処理終了"); await delay(1000); console.timeLog("log", "終了"); ``` %% > [!hint]- Hint 1 > 並行処理 ([[Promise.all (JavaScript)|Promise.all]]) が入ってもやることは同じです。[[Promise (JavaScript)|Promise]]を[[await (JavaScript)|await]]するだけ。 ## Mission 4 #😵HARD 以下の説明で誤っているものを**すべて**選び、その理由を説明してください。 1. [[await (JavaScript)|await]]演算子は[[async function (JavaScript)|async function]]の中でしか利用できない 2. `await Promise.all(promiseA, promiseB)` は `promiseA` と `promiseB` の両方が解決したら処理が先に進む書き方である 3. [[async function (JavaScript)|async function]]は必ず[[Promise (JavaScript)|Promise]]を返さなければいけないので、`return` する値は `Promise` オブジェクトでないといけない 4. `xs.map(async (x) => await fetch(x))` のようにして複数の非同期処理結果を[[Array.prototype.map (JavaScript)|Array.prototype.map]]を使って取得できる %% 解答例 すべて誤り。 1. [[モジュール (JavaScript)|モジュール]]のトップレベルでも利用できる 2. `Promise.all` の引数は[[配列 (JavaScript)|配列]]でなければけない `Promise.all([promiseA, promiseB])` 3. [[Promise (JavaScript)|Promise]]を返さなくても自動で[[Promise (JavaScript)|Promise]]にラップされるため、[[Promise (JavaScript)|Promise]]オブジェクトを`return`しなくてもよい 4. `xs.map(...)` の結果は [[Promise (JavaScript)|Promise]]の配列になるため、非同期処理の結果は取得できない %% --- *NEXT* >> [[📗TDQ-BOSS2 非同期が見える先の世界へ]]