ITスキル・ノウハウ

初心者向け:JavaScriptのasync / awaitを完全解説!

JavaScript-async-await

こんにちは、ラーメン好きなフリーランスエンジニアの”ぽんねぐ”です。

JavaScriptを学び始めると、すぐに「非同期処理」という言葉に出会います。APIからデータを取得したり、タイマーを使ったりする際に使われる技術ですが、初心者には少し難しく感じるかもしれません。

そんな非同期処理をシンプルに書くために登場したのが、async / await です。本記事では、基本的な使い方を、初心者にもわかりやすい用語解説付きで解説していきます!

本記事を最後まで読んで頂くことで以下のようなお悩みを解決できます。

以下の関連記事についても一緒にご確認頂けるとJavaScriptのことがもっと理解できます。

まずは簡単に私のことを紹介します。

プロフィール

・システムエンジニア歴:6年(会社員:5年、フリーランス:1年)

・退職後すぐに案件獲得に成功し、1年間継続中

・独立後の年収は正社員時代の2倍を達成

それでは始めていきましょう。

async / awaitとは?

非同期処理とは?

非同期処理とは、簡単に言うと「時間のかかる処理を待つ間に、他の作業を続けられるプログラミングの仕組み」です。

例えば次のようなケースです:

  • サーバーからデータを取得する(APIコール)
  • 画像や動画などの大きなファイルをダウンロードする
  • タイマーで一定時間後に何かを実行する

通常のコードでは、これらの処理が終わるまで待つ必要がありますが、非同期処理を使うと、プログラムが「他の仕事」を先に進めることができます。

asyncとは?

asyncは「非同期関数」を作るためのキーワードです。このキーワードをつけた関数は、常にPromiseという特別なオブジェクトを返します。

例:async関数の基本

JavaScript
async function greet() {
    return "Hello, World!";
}

greet().then(message => console.log(message)); // "Hello, World!"

上記の例では、async関数の戻り値は自動的にPromiseになります。つまり、関数がすぐに結果を返さず、「そのうち結果を渡すよ」と約束する形になります。

awaitとは?

awaitは、非同期処理の結果が返ってくるまで待機するキーワードです。ただし、awaitasync関数の中でしか使えません。

例:awaitの基本

JavaScript
async function fetchData() {
    const response = await fetch("https://api.example.com/data");
    const data = await response.json();
    console.log(data);
}

fetchData();
  • fetch関数: サーバーにリクエストを送り、データを取得するための関数です。
  • awaitの動作: fetchがデータを返すまで待機し、その結果をresponseに格納します。

Promiseとの違いを比較しよう

非同期処理の仕組みとしては、以前からPromiseがよく使われていました。しかし、コードが複雑になりやすいという課題がありました。

Promiseを使ったコード

JavaScript
fetch("https://api.example.com/data")
    .then(response => response.json())
    .then(data => console.log(data))
    .catch(error => console.error(error));

async / awaitを使ったコード

JavaScript
async function fetchData() {
    try {
        const response = await fetch("https://api.example.com/data");
        const data = await response.json();
        console.log(data);
    } catch (error) {
        console.error(error);
    }
}

fetchData();

ポイント

Promise.then().catch()を使って結果を処理しますが、async / awaitを使えば、非同期処理を同期処理のように記述できます。これにより、コードが読みやすく、ミスを減らすことができます。

async / awaitでのエラーハンドリング

非同期処理が失敗した場合(例えば、サーバーに接続できないなど)、エラーを適切に処理する必要があります。async / awaitでは、try-catch文を使います。

例:エラーハンドリング

JavaScript
async function fetchData() {
    try {
        const response = await fetch("https://api.example.com/data");
        if (!response.ok) {
            throw new Error("Network response was not ok");
        }
        const data = await response.json();
        console.log(data);
    } catch (error) {
        console.error("Fetch error:", error);
    }
}

fetchData();
  • tryブロック: 正常な処理を実行する。
  • catchブロック: エラーが発生した場合に処理を行う。

応用:複数の非同期処理を同時に扱う

複数の非同期処理を効率よく扱うには、Promise.allを使うのが一般的です。

例:複数の非同期処理を同時に実行

JavaScript
async function fetchMultipleData() {
    const urls = [
        "https://api.example.com/data1",
        "https://api.example.com/data2"
    ];
    try {
        const promises = urls.map(url => fetch(url).then(res => res.json()));
        const results = await Promise.all(promises);
        console.log(results);
    } catch (error) {
        console.error("Error fetching data:", error);
    }
}

fetchMultipleData();

解説:

  • map: 各URLに対してfetchを実行し、それぞれの結果をPromiseとして返します。
  • Promise.all: 配列内のすべてのPromiseが解決されるまで待機します。

async / awaitのメリット・デメリット

メリット

メリットは以下4つです。

コードの可読性が向上する

非同期処理を同期処理のように記述できるため、コードの流れが明確になります。
例えば、以下のPromiseasync / awaitの比較を見てください。

Promiseを使ったコード

JavaScript
fetch("https://api.example.com/data")
    .then(response => response.json())
    .then(data => console.log(data))
    .catch(error => console.error(error));

async / awaitを使ったコード

JavaScript
async function fetchData() {
    try {
        const response = await fetch("https://api.example.com/data");
        const data = await response.json();
        console.log(data);
    } catch (error) {
        console.error(error);
    }
}

fetchData();

ポイント: .then().catch()をネストする必要がなく、処理の流れが一目で理解しやすい。

エラーハンドリングが簡単

try-catch文を使えば、エラー処理を一箇所にまとめることができます。
これにより、複数の.catch()を使う必要がなくなり、コードの保守性が向上します。

デバッグがしやすい

非同期処理の中でも、awaitを使ったコードは同期処理のようにデバッグできます。
例えば、ステップごとに処理を追いやすく、ブラウザの開発者ツールでブレークポイントを設置しやすい点がメリットです。

複雑な非同期ロジックの実装が容易

複数の非同期処理を順番に実行する場合や、条件に応じて処理を切り替える場合も、async / awaitなら簡潔に記述できます。

デメリット

デメリットは以下4つです。

直列処理によるパフォーマンス低下の可能性

awaitを使うと、各非同期処理が完了するまで次の処理に進まないため、非効率になる場合があります。
例えば、次のコードでは非同期処理が直列的に実行され、全体の処理時間が長くなります。

JavaScript
async function fetchData() {
    const data1 = await fetch("https://api.example.com/data1");
    const data2 = await fetch("https://api.example.com/data2");
    console.log(data1, data2);
}

改善案:並列処理を使う

JavaScript
async function fetchData() {
    const [data1, data2] = await Promise.all([
        fetch("https://api.example.com/data1"),
        fetch("https://api.example.com/data2")
    ]);
    console.log(data1, data2);
}

async関数外では使えない

awaitasync関数の中でしか使えません。そのため、グローバルスコープでの利用が制限されます。

JavaScript
// エラー:SyntaxError: await is only valid in async function
const result = await fetch("https://api.example.com/data");

初心者には仕組みが分かりづらい

内部でPromiseを扱っているため、async / awaitの背後にある非同期処理の仕組みを理解していないと、意図しない挙動に戸惑うことがあります。

エラー処理を忘れるとバグの原因に

try-catchを使わずに非同期処理を書くと、エラーがキャッチされずに処理が失敗する可能性があります。
そのため、常にエラー処理を意識する必要があります。

まとめ

本記事では、Javascriptのasync/awaitについて解説しました。

async / awaitは、非同期処理を簡潔かつ直感的に記述できる方法です。これにより、複雑なコードも読みやすく、保守しやすくなります。

何か不明点などございましたらコメント欄に記載頂けたらと思います!

以上、最後までお読みいただきありがとうございました。

-ITスキル・ノウハウ