基礎から学ぶJavaScript入門 90日コース | 非同期処理とAPI - Day 66:非同期処理演習

JavaScript 90日で身につけるJavaScript
スポンサーリンク
スポンサーリンク

Day 66:非同期処理演習

Day 66では、第5フェーズ「非同期処理とAPI」の演習として、複数のAPIデータを扱う課題に取り組みます。ここまでの流れを振り返ると、Day 56で同期処理と非同期処理の違いを学び、Day 57でPromise、Day 58で async / await、Day 59で fetch API、Day 60で公開APIからのデータ取得、Day 61で画面表示、Day 62でローディング処理、Day 63でエラー処理、Day 64とDay 65で検索アプリの制作と改善を行ってきました。つまり、非同期処理の基本部品はすでにひと通り学んでいます。Day 66は、それらを少し広げて、「1つのAPIだけではなく、複数のAPIデータをどう扱うか」を考える回です。

初心者の方にとって、ここはとても重要な練習ポイントです。なぜなら、実際のWebアプリでは、1回の通信で必要な情報がすべてそろうとは限らないからです。たとえば、ある一覧データを取得したあとに、その関連情報を別のAPIから取ることがありますし、ユーザー情報と投稿情報のように、別々のデータを組み合わせて1つの画面を作ることもあります。つまり、現実のアプリは「1回 fetch() して終わり」ではない場面が多いです。その入口として、「複数のAPIデータを扱う」練習はとても価値があります。

ただし、初心者の方がここで急に難しく感じるのも自然です。なぜなら、データが2種類以上になると、「どの順番で取るのか」「別々の結果をどう結びつけるのか」「どこまで表示すればよいのか」「エラーが片方だけ失敗したらどうするのか」といった考えることが増えるからです。そこでDay 66では、いきなり高度な外部サービスAPIを使うのではなく、学習用の公開APIを使いながら、「複数のデータをどう整理し、どう結びつけて表示するか」をステップバイステップで学びます。今回のポイントは、難しいことを一気にやるのではなく、「1つずつ取得できる」「それぞれの形を確認できる」「対応するデータを結びつけられる」という順番で考えることです。

まず最初に結論をつかむ

最初に今回の内容を一言でまとめると、複数のAPIデータを扱うとは「別々に取得したデータを、必要に応じて組み合わせて1つの画面や結果にまとめること」です。

今回の基本的な流れは次のようになります。

  • 1つ目のAPIからデータを取得する
  • 2つ目のAPIからデータを取得する
  • それぞれのデータ構造を確認する
  • 共通する情報で結びつける
  • 組み合わせた結果を画面に表示する

初心者の方は、まず「複数のAPIを扱う」といっても、やることの基本は今までと同じで、取得・確認・加工・表示の流れを2つ以上に広げるだけだと理解すると安心しやすいです。

なぜ複数のAPIデータを扱う必要があるのか

ここはDay 66の中心です。なぜ複数のAPIデータを扱う必要があるのかというと、実際のアプリでは1種類のデータだけで十分とは限らないからです。たとえば、ユーザー一覧だけを見せるのではなく、そのユーザーの投稿一覧も見せたいことがあります。また、商品情報とレビュー情報、天気情報と地図情報、記事情報と著者情報のように、別々のデータを組み合わせて初めて意味のある画面になることもあります。

初心者の方は、「アプリの画面は、複数の情報の組み合わせでできることが多い」と考えると理解しやすいです。1つのAPIだけで完成するアプリもありますが、少し実用性が上がると複数データの扱いが必要になりやすいです。

今回の演習で扱う題材

Day 66では、初心者向けにわかりやすい題材として、「ユーザー情報」と「投稿情報」を組み合わせる例を扱います。たとえば、次の2つのAPIを使います。

  • ユーザー一覧API
  • 投稿一覧API

それぞれのURLは次のような形です。

https://jsonplaceholder.typicode.com/users
https://jsonplaceholder.typicode.com/posts
JavaScript

users にはユーザー情報が入り、posts には投稿情報が入っています。そして投稿データには userId が入っているため、「どの投稿がどのユーザーのものか」を結びつけることができます。

初心者の方は、ここで「共通の番号でデータ同士をつなげる」という考え方をつかむことが大切です。

複数データを扱うときに最初に考えること

複数のAPIデータを扱うとき、最初に考えるべきなのは「何と何を、何でつなぐのか」です。これをあいまいにしたままコードを書き始めると、途中で迷いやすくなります。

今回の題材なら、次のように整理できます。

  • ユーザー情報には `id` がある
  • 投稿情報には `userId` がある
  • `user.id` と `post.userId` が一致すれば、その投稿はそのユーザーのものだとわかる

初心者の方は、「複数データの扱い = 共通キーを探して結びつけること」と覚えると、かなり整理しやすいです。

ステップ1:まずは1つ目のAPIだけ確認する

いきなり2つのAPIを同時に扱うのではなく、最初は1つずつ確認するのが大切です。まずはユーザー一覧だけを取得します。

async function getUsers() {
  const response = await fetch("https://jsonplaceholder.typicode.com/users");
  const users = await response.json();

  console.log(users);
}

getUsers();
JavaScript

この段階では、ユーザー配列の形をよく見ます。初心者の方は、まず users が配列であり、その中の各要素がオブジェクトであること、そして id や name があることを確認するとよいです。

ステップ2:2つ目のAPIだけ確認する

次に、投稿一覧だけを取得して確認します。

async function getPosts() {
  const response = await fetch("https://jsonplaceholder.typicode.com/posts");
  const posts = await response.json();

  console.log(posts);
}

getPosts();
JavaScript

ここでは、投稿データの中に userId、id、title、body があることを見ます。初心者の方は、「投稿の中に userId があるから、ユーザーと結びつけられそうだ」と気づけると大きな前進です。

なぜ1つずつ確認するのか

この順番はとても大切です。複数のデータを扱うとき、いきなり全部混ぜると、何がどのデータなのかが見えにくくなります。まずは1つずつ中身を見ることで、それぞれの役割がはっきりします。

  • ユーザーは誰かを表す
  • 投稿は何を書いたかを表す
  • 投稿の `userId` でユーザーとつながる

初心者の方は、複数データを扱うときほど「個別確認」が大切だと覚えておくと、今後のAPI学習でも役立ちます。

ステップ3:2つのAPIを両方取得する

次に、1つの関数の中で両方のデータを取得します。まずは順番に取得する形でよいです。

async function loadData() {
  const usersResponse = await fetch("https://jsonplaceholder.typicode.com/users");
  const users = await usersResponse.json();

  const postsResponse = await fetch("https://jsonplaceholder.typicode.com/posts");
  const posts = await postsResponse.json();

  console.log(users);
  console.log(posts);
}

loadData();
JavaScript

この時点で、2種類の配列が1つの関数内にそろいます。初心者の方は、「今までは1つの配列だけだったが、今回は users と posts の2つがある」と意識すると整理しやすいです。

ステップ4:結びつける対象を決める

ここからが複数データらしい部分です。今回は、「各ユーザーについて、その人の投稿タイトルを表示する」という形を目標にします。つまり、1人のユーザーに対して、その人の投稿を探して一覧にするイメージです。

ここで必要なのは、「特定のユーザーの投稿だけを取り出す」ことです。そのために filter() を使います。

const userPosts = posts.filter(function (post) {
  return post.userId === user.id;
});
JavaScript

このコードの意味は、「投稿の中から、今見ているユーザーの id と一致する userId を持つものだけ取り出す」です。

初心者の方は、ここで Day 64とDay 65で学んだ検索の考え方が再登場していることに気づけるとよいです。今回も filter() が重要な役割をしています。

ステップ5:ユーザーと投稿をまとめて表示する

では、ユーザーごとに名前を表示し、その下に投稿タイトルを並べる形を考えます。まずはJavaScriptだけの流れを見ます。

users.forEach(function (user) {
  console.log("ユーザー名:", user.name);

  const userPosts = posts.filter(function (post) {
    return post.userId === user.id;
  });

  userPosts.forEach(function (post) {
    console.log("投稿タイトル:", post.title);
  });
});
JavaScript

このコードでは、外側でユーザーを1人ずつ見て、その中で対応する投稿だけを取り出して、さらに1件ずつ表示しています。初心者の方は、ここで「ループの中でさらに絞り込みをしている」流れをゆっくり追うことが大切です。

二重の処理をかみ砕いて理解する

この部分は初心者の方が少し混乱しやすいので、かみ砕いて整理します。

  • まずユーザーを1人選ぶ
  • そのユーザーの `id` を見る
  • 全投稿の中から、その `id` に合う投稿だけ探す
  • 見つかった投稿を順番に表示する

つまり、「ユーザーごとに関連投稿を探している」わけです。初心者の方は、ここを「箱の中から、その人専用の投稿を抜き出す作業」と考えるとイメージしやすいです。

ステップ6:HTMLに表示する

次に、これを実際に画面へ表示します。まずHTMLに表示先を用意します。

<h2>ユーザーと投稿一覧</h2>
<div id="output"></div>

そしてJavaScriptで表示を組み立てます。

async function loadData() {
  const output = document.getElementById("output");

  const usersResponse = await fetch("https://jsonplaceholder.typicode.com/users");
  const users = await usersResponse.json();

  const postsResponse = await fetch("https://jsonplaceholder.typicode.com/posts");
  const posts = await postsResponse.json();

  output.innerHTML = "";

  users.forEach(function (user) {
    // ユーザー名の見出しを作る
    const userTitle = document.createElement("h3");
    userTitle.textContent = user.name;
    output.appendChild(userTitle);

    // 投稿一覧用の ul を作る
    const ul = document.createElement("ul");

    // そのユーザーの投稿だけを取り出す
    const userPosts = posts.filter(function (post) {
      return post.userId === user.id;
    });

    // 投稿タイトルを li にして追加する
    userPosts.forEach(function (post) {
      const li = document.createElement("li");
      li.textContent = post.title;
      ul.appendChild(li);
    });

    output.appendChild(ul);
  });
}

loadData();
JavaScript

このコードで、各ユーザー名の下に、その人の投稿一覧が表示されます。初心者の方は、ここで「ユーザー名の見出し」と「投稿リスト」をセットで作っていることに注目すると理解しやすいです。

表示を作るときの考え方

複数データを扱うときは、表示も少し構造的に考える必要があります。今回は「親」と「子」の関係で考えるとわかりやすいです。

  • 親:ユーザー
  • 子:そのユーザーに対応する投稿

つまり、ユーザー1人が1つのまとまりで、その中に複数の投稿が入っている形です。初心者の方は、複数データを表示するときには「グループ構造」を意識すると整理しやすいです。

ローディング処理も組み込む

複数のAPIを取得するので、これまでより少し待ち時間が長くなる可能性があります。そのため、ローディング表示は特に大切です。

loading.style.display = "block";
JavaScript

取得完了後に消します。

loading.style.display = "none";
JavaScript

初心者の方は、データが1つでも2つでも、ローディングの考え方は同じだと理解すると安心です。ただし、今回は「全部の取得と表示が終わってから消す」ことが重要です。

エラー処理も忘れない

複数APIになると、片方だけ失敗することもありえます。まずは初心者向けに、どちらか1つでも失敗したら全体をエラー扱いする形で十分です。そのために try...catch を使います。

async function loadData() {
  try {
    const usersResponse = await fetch("https://jsonplaceholder.typicode.com/users");
    const users = await usersResponse.json();

    const postsResponse = await fetch("https://jsonplaceholder.typicode.com/posts");
    const posts = await postsResponse.json();

    console.log(users, posts);
  } catch (error) {
    console.log("データ取得に失敗しました。", error);
  }
}
JavaScript

初心者の方は、複数データだからといって特別なエラー処理を最初から考えすぎず、「まずはまとめて受け止める」形から始めれば十分です。

そのまま使える完成テンプレート

ここでは、Day 66の内容をまとめた完成テンプレートを紹介します。

<!DOCTYPE html>
<html lang="ja">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Day 66 非同期処理演習</title>
  <style>
    /* ローディング表示 */
    #loading {
      display: none;
      color: blue;
      font-weight: bold;
    }

    /* エラーメッセージ */
    .error {
      color: red;
    }

    /* ユーザーごとの表示ブロック */
    .user-card {
      margin-bottom: 24px;
      padding: 12px;
      border: 1px solid #ccc;
    }
  </style>
</head>
<body>
  <h2>ユーザーと投稿一覧</h2>

  <button id="loadButton">データを取得する</button>
  <p id="loading">データを読み込み中です...</p>
  <p id="message">ボタンを押すとユーザーと投稿一覧を表示します。</p>
  <div id="output"></div>

  <script>
    // 必要な要素を取得する
    const loadButton = document.getElementById("loadButton");
    const loading = document.getElementById("loading");
    const message = document.getElementById("message");
    const output = document.getElementById("output");

    // 複数のAPIデータを取得して表示する関数
    async function loadData() {
      try {
        // ローディング表示を出す
        loading.style.display = "block";

        // メッセージを初期化する
        message.textContent = "データを取得中です...";
        message.classList.remove("error");

        // 前回の表示を空にする
        output.innerHTML = "";

        // ユーザー一覧を取得する
        const usersResponse = await fetch("https://jsonplaceholder.typicode.com/users");

        // HTTPエラーを確認する
        if (!usersResponse.ok) {
          throw new Error("ユーザーデータの取得に失敗しました。");
        }

        const users = await usersResponse.json();

        // 投稿一覧を取得する
        const postsResponse = await fetch("https://jsonplaceholder.typicode.com/posts");

        // HTTPエラーを確認する
        if (!postsResponse.ok) {
          throw new Error("投稿データの取得に失敗しました。");
        }

        const posts = await postsResponse.json();

        // ユーザーごとに投稿をまとめて表示する
        users.forEach(function (user) {
          // ユーザー表示用の箱を作る
          const userCard = document.createElement("div");
          userCard.classList.add("user-card");

          // ユーザー名の見出しを作る
          const userTitle = document.createElement("h3");
          userTitle.textContent = user.name;
          userCard.appendChild(userTitle);

          // メールアドレスも表示する
          const email = document.createElement("p");
          email.textContent = "メール: " + user.email;
          userCard.appendChild(email);

          // 投稿一覧の ul を作る
          const ul = document.createElement("ul");

          // そのユーザーに対応する投稿だけを抽出する
          const userPosts = posts.filter(function (post) {
            return post.userId === user.id;
          });

          // 投稿タイトルを li にして追加する
          userPosts.forEach(function (post) {
            const li = document.createElement("li");
            li.textContent = post.title;
            ul.appendChild(li);
          });

          // 投稿一覧をユーザーカードに追加する
          userCard.appendChild(ul);

          // ユーザーカードを画面に追加する
          output.appendChild(userCard);
        });

        // 成功メッセージ
        message.textContent = "ユーザーと投稿データの取得に成功しました。";
      } catch (error) {
        // エラー時の表示
        message.textContent = "データの取得に失敗しました。時間をおいて再試行してください。";
        message.classList.add("error");

        // 開発者向けに詳細を表示する
        console.log("エラー詳細:", error);
      }

      // ローディング表示を消す
      loading.style.display = "none";
    }

    // ボタンが押されたらデータ取得を実行する
    loadButton.addEventListener("click", function () {
      loadData();
    });
  </script>
</body>
</html>

この完成コードの見どころ

初心者の方は、完成コードの中で次の点を重点的に見ると理解しやすいです。

  • APIを2回 `fetch()` していること
  • `users` と `posts` が別々の配列として存在していること
  • `post.userId === user.id` で結びつけていること
  • ユーザーごとに投稿一覧を作っていること
  • ローディングやエラー処理もこれまで通り入っていること

特に重要なのは、「APIが増えてもやることの基本は変わらない」という点です。取得して、確認して、必要に応じて絞り込み、表示します。

さらに理解を深めるための練習例

Day 66の内容をより定着させるためには、少しだけ条件を変えてみるのがおすすめです。

  • 各ユーザーについて、最初の3件の投稿だけ表示する
  • ユーザー名だけでなく会社名も表示する
  • 投稿タイトルではなく投稿本文も一部表示する
  • 投稿件数をユーザー名の横に表示する

たとえば投稿件数なら、userPosts.length を使えば表示できます。初心者の方は、こうした小さな改造を通して、「取得した複数データをどう加工して見せるか」の感覚が育っていきます。

情報セキュリティの観点での注意

複数のAPIデータを扱うと、表示する情報量が増えます。そのため、どのデータを画面へ出すかをより慎重に考える必要があります。学習用APIでは大きな問題になりにくいですが、実際の開発では、外部データをそのまま無制限に表示するのは避けるべき場面もあります。また、取得するデータが増えるほど通信失敗や想定外データの影響も受けやすくなるため、エラー処理や表示方法を丁寧に設計することが重要です。初心者の方は、「外部データは便利だが、量が増えるほど確認と整理が大事になる」と意識すると、堅実な開発感覚につながります。

初心者が特に注意したいポイント

Day 66では、次の点を特に意識すると理解が深まりやすいです。

  • 複数のAPIデータは、まず1つずつ確認することが大切です。
  • データ同士を結びつける共通キーを探すことが重要です。
  • 今回は `user.id` と `post.userId` が対応しています。
  • 取得したあとに `filter()` で対応データを探す流れが基本です。
  • ローディングとエラー処理は複数APIでも必要です。

特に重要なのは、「複数のデータを扱う」と聞いて身構えすぎず、「今までの取得・確認・表示を2種類に広げるだけ」と考えることです。

Day 66のゴールを整理する

Day 66では、次のことができるようになっていれば十分です。

  • 複数のAPIからデータを取得できる
  • それぞれのデータ構造を確認できる
  • 共通するキーでデータ同士を結びつけられる
  • 組み合わせた情報をHTMLに表示できる
  • ローディングとエラー処理を含めた形で実装できる

ここまでできれば、Day 66としてはとても順調です。非同期処理の基礎を1つ上の実践へ広げられています。

まとめ

Day 66では、第5フェーズ「非同期処理とAPI」の演習として、複数のAPIデータを扱う課題に取り組みました。今回は、ユーザー一覧と投稿一覧という2種類の公開APIデータを取得し、それぞれの構造を確認したうえで、user.id と post.userId を共通キーとして結びつけました。そして、各ユーザーの下にその人の投稿一覧を表示する形で、複数データを1つの画面にまとめる流れを学びました。

特に大切なのは、複数のAPIを扱うからといって急に特別なことをしているわけではなく、これまで学んだ「取得する」「中身を見る」「必要な条件で絞り込む」「画面へ表示する」という基本を、複数データに広げているだけだという点です。そして、データ同士を結びつけるためには、どのキーが対応しているのかを最初に整理することが重要です。

次に進む前に、今回のテンプレートを実際に動かし、ユーザー名の横に投稿件数を出したり、投稿本文を少し表示したりと、小さな改造を試してみるのがおすすめです。そして、「どのデータを、どのキーで、どう結びつけているのか」を自分の言葉で説明できるようになると、Day 66の理解がかなり深まっています。

タイトルとURLをコピーしました