0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

async/awaitについて今一度整理する

Last updated at Posted at 2025-05-15

きっかけ

久しぶりにreactを使ってwebアプリケーションを開発したのだが、async/awaitの使い方を忘れていた。なので、もう一度学び直したい。

async/awaitとは

async/await は、JavaScript の非同期処理をシンプルに書くための構文。非同期処理を同期的な見た目で記述できるため、コードの可読性が高まる。

  • async を付けた関数は自動的に Promise を返す。
  • await を使うと、Promise が解決されるまで処理を一時停止できる。ただし awaitasync 関数の中でしか使えない。

本番のコードを使って解説

以下のコードは、現在地と天気情報を取得し、それをもとに詩を生成する React アプリの一部。非同期処理に async/await を多用しているため、順を追って解説する。

1. fetchPoem 関数の定義

const fetchPoem = async () => { ... }

ここで async を付けているため、関数内で await を使えるようになる。

2. 現在地を取得

const position = await new Promise<GeolocationPosition>((resolve, reject) => {
  navigator.geolocation.getCurrentPosition(resolve, reject);
});

getCurrentPositionコールバック形式なので、Promise にラップして await で扱えるようにしている。

3. 天気APIの呼び出し

const weatherRes = await fetch(`https://api.openweathermap.org/...`);
const weatherData = await weatherRes.json();

fetch は非同期関数。レスポンスが返るまで待ち、次に response.json()await で待ってからデータを取得する。

4. 詩の生成APIの呼び出し

const res = await fetch("/api/GeneratePoem", {
  method: "POST",
  headers: { "Content-Type": "application/json" },
  body: JSON.stringify(environment),
});
const data = await res.json();

ここでも await を使って、API呼び出しからレスポンスの取得、JSON 変換までを順番に処理している。

5. try-catch-finally でのエラーハンドリング

try {
  ...
} catch (error) {
  console.error(error);
  setPoem("詩の取得に失敗しました");
} finally {
  setIsLoading(false);
}

非同期処理のどこかでエラーが起きても、catch でエラーメッセージを出力し、最後にローディング状態を解除するようにしている。

6. 実行順番

  1. コンポーネント Home の初回レンダリング

  2. useEffect により fetchPoem() が一度だけ呼ばれる

  3. fetchPoem() 内で以下の非同期処理が順番に実行される:

    1. ブラウザから位置情報を取得(navigator.geolocation.getCurrentPosition
    2. OpenWeather API で天気情報を取得(fetch
    3. weatherRes.json() でレスポンスをパース
    4. 環境情報を整形
    5. 自作 API /api/GeneratePoem に POST リクエスト
    6. 詩のデータを取得し、setPoem() で更新
  4. finally 節で setIsLoading(false) を実行

  5. poem または isLoading の state 変更により再レンダリング

コード全文

'use client'; // このファイルはクライアントコンポーネントとして扱う

import { useEffect, useState } from "react";
import Header from "./components/layouts/Header/Header";
import Footer from "./components/layouts/Footer/Footer";
import CoccoCharacter from "./features/home/components/CoccoCharacter/CoccoCharacter";
import PoemBubble from "./features/home/components/PoemBubble/PoemBubble";

// Home コンポーネントの定義
export default function Home() {
  // 詩の内容を保持する state
  const [poem, setPoem] = useState("");
  // 読み込み中かどうかを示す state
  const [isLoading, setIsLoading] = useState(true);

  // コンポーネントの初回マウント時に実行
  useEffect(() => {
    // 詩を取得する非同期関数
    const fetchPoem = async () => {
      try {
        // 現在の位置情報を取得(Promise にラップして await を使用可能に)
        const position = await new Promise<GeolocationPosition>((resolve, reject) => {
          navigator.geolocation.getCurrentPosition(resolve, reject);
        });

        // 緯度・経度の取得
        const lat = position.coords.latitude;
        const lon = position.coords.longitude;

        // OpenWeather API を使って現在地の天気情報を取得
        const weatherRes = await fetch(
          `https://api.openweathermap.org/data/2.5/weather?lat=${lat}&lon=${lon}&appid=${process.env.NEXT_PUBLIC_WEATHER_API_KEY}&units=metric&lang=ja`
        );

        // 天気情報を JSON に変換
        const weatherData = await weatherRes.json();

        // 詩の生成に必要な環境データを整形
        const environment = {
          location: weatherData.name, // 地名
          temperature: weatherData.main.temp, // 気温
          humidity: weatherData.main.humidity, // 湿度
          weather: weatherData.weather[0].description, // 天気の説明(例:くもり、晴れなど)
          time: new Date().toLocaleString("sv-SE", { timeZone: "Asia/Tokyo" }).replace(" ", "T") + ":00.000+09:00", // ISO8601形式に近い日時
        };

        // サーバーサイドのAPIに POST リクエストを送って詩を生成
        const res = await fetch("/api/GeneratePoem", {
          method: "POST",
          headers: {
            "Content-Type": "application/json",
          },
          body: JSON.stringify(environment), // 環境情報を送信
        });

        // レスポンスを JSON として受け取り、詩のテキストを抽出
        const data = await res.json();
        setPoem(data.text || "詩の取得に失敗しました"); // テキストがなければエラーメッセージを表示
      } catch (error) {
        // 位置情報取得やAPI呼び出しでエラーが発生した場合
        console.error(error);
        setPoem("詩の取得に失敗しました");
      } finally {
        // 読み込み状態を終了
        setIsLoading(false);
      }
    };

    // 詩の取得関数を実行
    fetchPoem();
  }, []); // 空配列のため、初回マウント時のみ実行

  // JSXの返却(UIレンダリング)
  return (
    <>
      <Header /> {/* ヘッダーコンポーネント */}
      <main className="min-h-screen bg-[#40494F] text-white flex flex-col">
        <div className="flex-grow flex items-start justify-center pt-16 sm:pt-32">
          <div className="flex flex-col items-center gap-4 sm:gap-6">
            {/* 詩の吹き出しコンポーネント。読み込み中は「考え中...」と表示 */}
            <PoemBubble poem={isLoading ? "考え中..." : poem} />
            {/* キャラクターのアニメーションなどに使用。読み込み状態を渡す */}
            <CoccoCharacter isLoading={isLoading} />
          </div>
        </div>
      </main>
      <Footer /> {/* フッターコンポーネント */}
    </>
  );
}

0
0
1

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?