ここまで計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 非同期が見える先の世界へ]]