async/await

エイシンク/アウェイト

Async を分かりやすく

Async(エイシンク/アウェイト)は、JavaScript および TypeScript で非同期処理を行う際の概念です。なんていうと難しく聞こえますよね?

でも実際はそんなに難しくありません。非同期っていうのは、具体的には「処理がすぐには終わらないもの」を指します。データベースからのデータ取得や、APIからのレスポンス待ちなんかがそれに該当します。

普通に考えると、処理が終わるまで待つのは当たり前じゃないですか?でも、JavaScriptは基本的に「単一スレッド」で動いているので、処理が終わるまで待っていたら、他の処理が止まってしまうんです。それはまるでレストランで一人のウェイターが全てのテーブルを担当している状況と似ています。一つのテーブルの注文を取り終わるまで他のテーブルの注文を取れないんですよね。

ここでasync/awaitの出番です。これを使うと、JavaScriptが「ウェイターが注文を取りながら、他のテーブルの注文も同時に取れるように」なるんです。

async function ウェイター() {
    const 注文1 = await メニューから選ぶ(); // ここで注文を待つ
    const 注文2 = await メニューから選ぶ(); // ここで次の注文を待つ
    // ...
}

ここで、asyncは「この関数は非同期で動くよ」と宣言しています。そして、awaitは「この行の処理が終わるまで待ってね」と伝えています。でも、「待ってる間に他の処理も進めていいよ」とも言っているんですね。

なので、ウェイターは1つ目のテーブルの注文を取りながら、2つ目のテーブルの注文も同時に進めることができます。すごい便利ですよね?

ただし、awaitは「Promise」を返す関数に対してしか使えません。Promiseは「将来的に結果を返す」ことを約束したものです。それはちょっと、注文をしたけど料理がまだ出てこない状態に似ています。でも大丈夫、待っていればきっと出てきますよ。

Next.jsやJamstackのようなフレームワークでは、非同期処理は必須ですから、async/awaitはとても重要な知識となるんです。だから、ちょっと頭が痛くなっても覚えておく価値はあるんですよ。あなたもJavaScriptのウェイターとして、色んなテーブル(タスク)を巧みにこなせるようになるといいですね!

少しまとめると、非同期処理は、処理が完了するまで待機せずに、次の処理に進むことができる特徴があります。これにより、重い処理や API の通信など、時間がかかる処理を効率的に行うことができます。

Async を使うメリット

  1. レスポンシブなアプリケーション: 非同期処理を用いることで、ユーザーの操作をブロックせずに処理を行うことができ、アプリケーションの応答性を向上させます。
  2. パフォーマンス向上: 非同期処理により、時間のかかる処理をバックグラウンドで実行させることができるため、アプリケーション全体のパフォーマンスが向上します。 3.### コードの可読性: async/await を用いることで、非同期処理を分かりやすく記述することができます。

Server Components

Next.js v13 の Server Components で async/await を使うパターンです。async および を使用して、await サーバー コンポーネントのデータを取得できます。

async function getData() {
  const res = await fetch('https://api.example.com/...')
  if (!res.ok) {
    throw new Error('Failed to fetch data')
  }

  return res.json()
}

export default async function Page() {
  const data = await getData()

  return <main></main>
}

getData 関数: この非同期関数は、https://api.example.com/... という URL からデータを取得する役割を持ちます。fetch を使って API リクエストを行い、レスポンスが成功 (res.ok が true) であれば、JSON データを返します。もしレスポンスが失敗した場合、エラーを投げます。

Page 関数: この非同期関数は、Next.js のページコンポーネントを定義しています。getData 関数を await で呼び出してデータを取得し、data に格納します。その後、<main>タグを返しています。

TypeScript エラー「'Promise<Element>' is not a valid JSX element」が出たら、{/* @ts-expect-error Async Server Component */} をファイルの先頭に追加してタイプチェックを無効にすることができます。

Data Fetching: Fetching | Next.js

Async を実装

Next.js と TypeScript を用いた非同期処理の実装例を見ていきましょう。ここでは、外部 API からデータを取得し、そのデータを表示するコンポーネントを作成します。

まず、API からデータを取得する関数を作成します。fetchData という関数を作り、外部 API からデータを非同期で取得するようにします。

// utils/fetchData.ts
async function fetchData(url: string): Promise<any> {
  const response = await fetch(url)
  const data = await response.json()
  return data
}

export default fetchData

次に、データを表示するコンポーネントを作成します。このコンポーネントでは、useEffectuseState を用いて、データを非同期で取得し、表示します。

// components/AsyncComponent.tsx
import { useState, useEffect } from 'react'
import fetchData from '../utils/fetchData'

type Props = {
  url: string
}

const AsyncComponent = ({ url }: Props) => {
  const [data, setData] = useState<any>(null)
  const [loading, setLoading] = useState<boolean>(true)

  useEffect(() => {
    const getData = async () => {
      setLoading(true)
      const result = await fetchData(url)
      setData(result)
      setLoading(false)
    }
    getData()
  }, [url])

  if (loading) {
    return <div>Loading...</div>
  }

  return (
    <div>
      <h3>Data from API:</h3>
      <pre>{JSON.stringify(data, null, 2)}</pre>
    </div>
  )
}

export default AsyncComponent

補足情報

上記の AsyncComponent は、url という Props を受け取り、その URL に対して非同期で API リクエストを行っています。useState を用いて、データと読み込み状態を管理しています。

useEffect は、コンポーネントがマウントされたタイミングで実行される副作用関数です。ここで定義された getData 関数は、fetchData 関数を呼び出して非同期で API からデータを取得し、その結果を setDatadata にセットします。また、処理が終了したら setLoading を使って読み込み状態を false に更新しています。

最後に、loading の状態に応じて、読み込み中のメッセージを表示するか、取得したデータを表示するかを決定しています。

以上のコードにより、Next.js と TypeScript を使った非同期処理の実装ができます。Async を用いることで、効率的に非同期処理を行い、アプリケーションのレスポンシブ性やパフォーマンスを向上させることができます。また、コードの可読性も向上し、メンテナンス性が高まります。