JavaScriptの非同期処理とは?Promiseとasync/awaitの基礎

JavaScriptの非同期処理をイメージした処理の流れと並列実行のビジュアル
JavaScriptにおける非同期処理の流れと、Promise・async/awaitによる実行イメージを表現したアイキャッチ画像

JavaScriptでは、時間のかかる処理を効率よく扱うために「非同期処理」という仕組みが用意されています。API通信やファイルの読み込みなど、すぐに結果が返らない処理において重要な概念です。

本記事では、非同期処理の基本から、Promise・async/awaitの使い方までを体系的に解説します。基礎を理解することで、より実務に近いコードが書けるようになります。

非同期処理とは何か

非同期処理とは、「処理の完了を待たずに次の処理を進める仕組み」です。

通常の処理(同期処理)では、1つの処理が終わるまで次の処理は実行されません。一方、非同期処理では時間のかかる処理を裏で実行しながら、他の処理を先に進めることができます。

同期処理の例

console.log("処理1");
console.log("処理2");
console.log("処理3");

実行結果:

処理1
処理2
処理3

順番どおりに実行されます。

非同期処理の例

console.log("処理1");

setTimeout(() => {
  console.log("処理2(遅れて実行)");
}, 1000);

console.log("処理3");

実行結果:

処理1
処理3
処理2(遅れて実行)

このように、非同期処理では順番が前後することがあります。

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

非同期処理が必要になる理由は、主に以下の通りです。

  • API通信(サーバーとのやり取り)
  • ファイルの読み込み
  • タイマー処理
  • ユーザー操作の待機

これらをすべて同期処理で行うと、処理が止まり、画面が固まる原因になります。

なお、JavaScriptの基本的な仕組みについては、以下の記事で整理できます。
👉 JavaScriptの基本構造とは?初心者向けに仕組みを解説

コールバック関数による非同期処理

初期のJavaScriptでは、非同期処理はコールバック関数で実装されていました。

コールバックの例

function fetchData(callback) {
  setTimeout(() => {
    callback("データ取得完了");
  }, 1000);
}

fetchData((result) => {
  console.log(result);
});

問題点(コールバック地獄)

処理が増えると、ネストが深くなり可読性が悪くなります。

setTimeout(() => {
  console.log("1");
  setTimeout(() => {
    console.log("2");
    setTimeout(() => {
      console.log("3");
    }, 1000);
  }, 1000);
}, 1000);

この問題を解決するために登場したのがPromiseです。

👉 JavaScriptのコールバック関数とは?基本から解説

Promiseとは何か

Promiseとは、「非同期処理の結果を表すオブジェクト」です。

状態は3つあります。

  • pending(待機中)
  • fulfilled(成功)
  • rejected(失敗)

Promiseの基本構文

const promise = new Promise((resolve, reject) => {
  setTimeout(() => {
    resolve("成功");
  }, 1000);
});

promise.then((result) => {
  console.log(result);
});

エラーハンドリング

const promise = new Promise((resolve, reject) => {
  const success = false;

  if (success) {
    resolve("成功");
  } else {
    reject("失敗");
  }
});

promise
  .then((result) => {
    console.log(result);
  })
  .catch((error) => {
    console.error(error);
  });

Promiseのチェーン処理

複数の非同期処理をつなげることができます。

function step1() {
  return new Promise((resolve) => {
    setTimeout(() => resolve("ステップ1完了"), 1000);
  });
}

function step2() {
  return new Promise((resolve) => {
    setTimeout(() => resolve("ステップ2完了"), 1000);
  });
}

step1()
  .then((result) => {
    console.log(result);
    return step2();
  })
  .then((result) => {
    console.log(result);
  });

async/awaitとは何か

async/awaitは、Promiseをよりシンプルに書くための構文です。

基本構文

function delay() {
  return new Promise((resolve) => {
    setTimeout(() => {
      resolve("完了");
    }, 1000);
  });
}

async function main() {
  const result = await delay();
  console.log(result);
}

main();

ポイント

  • async関数はPromiseを返す
  • awaitはPromiseの完了を待つ

try/catchによるエラーハンドリング

function fetchData() {
  return new Promise((resolve, reject) => {
    const success = false;

    if (success) {
      resolve("データ取得成功");
    } else {
      reject("エラー発生");
    }
  });
}

async function main() {
  try {
    const result = await fetchData();
    console.log(result);
  } catch (error) {
    console.error(error);
  }
}

main();

Promiseとasync/awaitの違い

項目Promiseasync/await
書き方thenチェーン同期的に見える
可読性やや低い高い
エラー処理catchtry/catch

実務では、基本的にasync/awaitがよく使われます。

並列処理(Promise.all)

複数の非同期処理を同時に実行できます。

function task(name, time) {
  return new Promise((resolve) => {
    setTimeout(() => {
      resolve(name);
    }, time);
  });
}

async function main() {
  const results = await Promise.all([
    task("A", 1000),
    task("B", 2000),
    task("C", 1500)
  ]);

  console.log(results);
}

main();

非同期処理を理解するためのポイント

  • 非同期処理は「待たない処理」
  • Promiseは「結果を扱う仕組み」
  • async/awaitは「書きやすくする構文」

これらをセットで理解することが重要です。

また、関数の基礎を理解しておくと理解が深まります。
👉 JavaScriptの関数とは?基本と使い方

まとめ

JavaScriptの非同期処理は、現代のWeb開発において欠かせない概念です。

  • 非同期処理は処理を止めないための仕組み
  • Promiseで非同期処理を管理する
  • async/awaitでシンプルに書ける

基礎を押さえることで、API通信や実務コードの理解が一気に進みます。

関連記事