![[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 オプショナルチェーン]]