async/awaitとは?日常例で理解する非同期処理の基本

💻その他

こんにちは、とまだです。

みなさん、JavaScriptで非同期処理を書くとき、「なんだか複雑だな」と感じたことはありませんか?

今回は現役のエンジニア、そして元プログラミングスクール講師としての経験から、async/awaitについて解説します。

非同期処理の必要性

なぜ非同期処理が必要なのか

JavaScriptで外部APIを呼び出すとき。

データの取得に時間がかかりますよね。

もし、この待ち時間で画面が固まったら?

ユーザーは困ってしまいます。

そこで登場するのが非同期処理です。

日常生活で例えると

非同期処理は料理に似ています。

ご飯を炊きながら、おかずも作る。

同時に複数の作業を進められます。

一方、同期処理は一列に並ぶ自動販売機。

前の人の購入が終わるまで待つ必要があります。

コールバックからPromiseへの進化

コールバック地獄の問題

以前はコールバック関数を使っていました。

でも、処理が増えるとネストが深くなります。

getData(function(result1) {
  processData(result1, function(result2) {
    saveData(result2, function(result3) {
      // さらに続く...
    });
  });
});

このコードは読みづらいですよね。

エラー処理も複雑になってしまいます。

Promiseで改善

Promiseの登場で、処理が連鎖的に書けるようになりました。

getData()
  .then(result1 => processData(result1))
  .then(result2 => saveData(result2))
  .catch(error => console.error(error));

コールバックよりは読みやすくなりました。

でも、まだ完璧ではありません。

async/awaitの基本

async関数の定義

関数の前にasyncを付けるだけです。

async function fetchUserData() {
  // 非同期処理を書く
}

これで非同期関数の完成です。

中でawaitが使えるようになります。

awaitの使い方

awaitはPromiseの結果を待ちます。

async function getUserInfo() {
  const response = await fetch('/api/user');
  const data = await response.json();
  return data;
}

見た目は同期的なコードと同じです。

でも実際は非同期で動いています。

実務での活用例

API通信の実装

実際のWebアプリケーションでよく使う例です。

async function fetchWeatherData(city) {
  try {
    const response = await fetch(`/api/weather?city=${city}`);

    if (!response.ok) {
      throw new Error('天気データの取得に失敗しました');
    }

    const weatherData = await response.json();
    return weatherData;
  } catch (error) {
    console.error('エラーが発生しました:', error);
    throw error;
  }
}

エラーハンドリングも含めて、シンプルに書けます。

実務では、このようなAPI通信が頻繁に発生します。

複数の非同期処理

複数のAPIを同時に呼ぶこともあります。

async function loadDashboardData() {
  try {
    // 並列で実行
    const [users, posts, comments] = await Promise.all([
      fetch('/api/users').then(res => res.json()),
      fetch('/api/posts').then(res => res.json()),
      fetch('/api/comments').then(res => res.json())
    ]);

    return { users, posts, comments };
  } catch (error) {
    console.error('データ取得エラー:', error);
  }
}

Promise.allを使えば、並列処理も簡単です。

すべての処理が完了するまで待ちます。

よくある疑問と注意点

エラーハンドリングの方法

try-catchを使ってエラーを捕捉します。

async function safeApiCall() {
  try {
    const result = await riskyOperation();
    return result;
  } catch (error) {
    // エラー時の処理
    console.error('処理に失敗しました:', error.message);
    // デフォルト値を返す
    return null;
  }
}

エラーが起きても、アプリが止まりません。

適切な処理を続けられます。

forループ内でのawait

ループ内でawaitを使う際は注意が必要です。

// 順番に処理(遅い)
async function processSequentially(items) {
  for (const item of items) {
    await processItem(item);
  }
}

// 並列に処理(速い)
async function processInParallel(items) {
  await Promise.all(
    items.map(item => processItem(item))
  );
}

状況に応じて使い分けましょう。

並列処理の方が高速ですが、APIの制限に注意です。

async関数の戻り値

async関数は必ずPromiseを返します。

async function getValue() {
  return 42;
}

// 呼び出し側
getValue().then(value => {
  console.log(value); // 42
});

// またはasync関数内で
async function main() {
  const value = await getValue();
  console.log(value); // 42
}

この仕様を理解しておくことが大切です。

混乱を避けられます。

まとめ

async/awaitは非同期処理を簡潔に書ける構文です。

コールバックやPromiseより読みやすく、メンテナンスしやすいコードが書けます。

エラーハンドリングもtry-catchで直感的に。

並列処理もPromise.allで実現できます。

初心者の方も、基本的な使い方から始めて。

徐々に応用的な使い方を覚えていけば大丈夫です。

実際のプロジェクトで使いながら、理解を深めていってください。

共有:

著者について

とまだ

とまだ

フルスタックエンジニア

Learning Next の創設者。Ruby on Rails と React を中心に、プログラミング教育に情熱を注いでいます。初心者が楽しく学べる環境作りを目指しています。

著者の詳細を見る →