![[tdq-boss2.webp|cover-picture]]
非同期処理に立ち向かうための大いなる武器、[[Promise (JavaScript)|Promise]]と[[async function (JavaScript)|async function]]、[[await (JavaScript)|await]]を手に、ここが[[JavaScript]]の世界の入り口です。非同期処理をメインとして実践的な演習を乗り越えましょう。
## Mission 1
#🙂NORMAL
https://reqres.in/api/products のURLを利用して、プロダクトの**合計件数**を出力するコードを書いてください。APIリクエストには[[Fetch API]]を使ってください。
> [!attention] 注意
> - 例外処理は不要です
> - コードをシンプルにするため冗長なリクエストをしても大丈夫です
> - 外部ライブラリは**使用禁止**です
%%
解答例
```js
const res = await fetch("https://reqres.in/api/products");
const products = await res.json();
console.log(products.total);
```
%%
> [!hint]- Hint 1
> 何が[[Promise (JavaScript)|Promise]]を返すのかをしっかり見極めましょう。[[IDE]]から型定義が確認できるはずです。(本当は[[TypeScript]]用ですが...)
## Mission 2
#😵HARD
https://reqres.in/api/products?per_page=3 のURLを利用して、すべてのプロダクトデータを取得するまで **直列に** リクエストし、結果を **プロダクト名の[[配列 (JavaScript)|配列]]** で返却するコードを書いてください。
> [!attention] 注意
> - `per_page` の値は**変更禁止です**
> - `page` [[クエリパラメータ]]を追加すると1page目以降の結果を取得できます
> - 例外処理は不要です
> - コードをシンプルにするため冗長なリクエストをしても大丈夫です
> - 外部ライブラリは**使用禁止**です
%%
解答例
```js
async function fetchProducts(page) {
const res = await fetch(
`https://reqres.in/api/products?per_page=3&page=${page}`,
);
return await res.json();
}
const totalPages = (await fetchProducts()).total_pages;
const productNames = [];
for (let i = 0; i < totalPages; i++) {
const names = (await fetchProducts(i + 1)).data.map((x) => x.name);
productNames.push(...names);
}
console.log(productNames);
```
%%
> [!hint]- Hint 1
> 非同期処理を直列に実行する場合は [[for...of (JavaScript)|for...of]] が分かりやすくてオススメです。
## Mission 3
#😵HARD
[[#Mission 2]]を非同期処理が**すべて並列**になるように書き換えてください。
> [!attention] 注意
> - 制約は[[#Mission 2]]と同じです
%%
解答例
```js
async function fetchProducts(page) {
const res = await fetch(
`https://reqres.in/api/products?per_page=3&page=${page}`,
);
return await res.json();
}
function range(n) {
return [...Array(n).keys()];
}
const totalPages = (await fetchProducts()).total_pages;
const promises = range(totalPages)
.map((n) => fetchProducts(n + 1));
const productNames = (await Promise.all(promises)).flatMap((x) =>
x.data.map((d) => d.name)
);
console.log(productNames);
```
%%
## Mission 4
#😱NIGHTMARE
[[#Mission 3]]を **常に2つのワーカーがリクエストするように** 書き換えてください。また、リクエストは https://reqres.in/api/products?per_page=2 を使ってください。
> [!attention] 注意
> - `per_page` の値は**変更禁止です**
> - API取得処理は**ランダムで**遅延が発生するようにしてください
> - `page` [[クエリパラメータ]]を追加すると1page目以降の結果を取得できます
> - 例外処理は不要です
> - コードをシンプルにするため冗長なリクエストをしても大丈夫です
%%
解答例
```js
import pLimit from "npm:p-limit";
function delay(ms) {
return new Promise((resolve) => {
setTimeout(resolve, ms);
});
}
async function fetchProducts(page) {
await delay(100 + Math.random() * 2000);
const res = await fetch(
`https://reqres.in/api/products?per_page=2&page=${page}`,
);
return await res.json();
}
function range(n) {
return [...Array(n).keys()];
}
const totalPages = (await fetchProducts()).total_pages;
const limit = pLimit(2);
const promises = range(totalPages).map((n) =>
limit(() => fetchProducts(n + 1))
);
const productNames = (await Promise.all(promises)).flatMap((x) =>
x.data.map((d) => d.name)
);
console.log(productNames);
```
%%
> [!hint]- Hint 1
> [[p-limit]]を使うと楽です。
> [!hint]- Hint 2
> [[Deno]]で[[npm]]モジュールを使うには `import pLimit from "npm:p-limit";` のように記述します。あとは[[IDE]]のクイックフィックスなどでインストールできるはず。
---
*NEXT* >> [[📗TDQ-028 オプショナルチェーン]]