JavaScriptの非同期処理とは?基本をわかりやすく解説

JavaScriptの非同期処理を表す、複数の処理が並行して進み最終的に1つの結果にまとめられるイメージ
JavaScriptの非同期処理の仕組みを視覚的に表現したアイキャッチ画像です。複数の処理が同時に進み、Promiseやasync/awaitによって整理されて1つの結果にまとまる様子を表しています。

JavaScriptを学んでいると、「非同期処理」という言葉を頻繁に目にします。
API通信やファイルの読み込み、タイマー処理など、多くの場面で使われる重要な仕組みです。

しかし、次のような疑問を持つことも少なくありません。

  • 非同期処理とは何か
  • 同期処理と何が違うのか
  • なぜ処理の順番が思った通りにならないのか
  • Promiseやasync/awaitとどう関係するのか

この記事では、JavaScriptの非同期処理の基本を、仕組みから具体例まで丁寧に解説します。
この記事を読むことで、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

setTimeout() の処理は1秒後に実行されるため、その完了を待たずに次の console.log("3") が先に実行されます。

非同期処理が必要な理由

時間のかかる処理を待ち続けると、プログラム全体が停止したように見えてしまいます。

非同期処理を使うことで、他の処理を止めずに実行できます。

非同期処理が使われる例

  • API通信
  • ファイルの読み込み
  • 画像の読み込み
  • タイマー処理
  • データベースアクセス

同期処理と非同期処理の違い

項目同期処理非同期処理
実行順序完了してから次へ進む完了を待たずに次へ進む
処理速度遅い処理で全体が止まる他の処理を継続できる
主な用途計算や通常処理通信や読み込み

setTimeout()で学ぶ非同期処理

setTimeout() は、一定時間後に処理を実行する代表的な非同期処理です。

console.log("開始");

setTimeout(() => {
  console.log("1秒後に実行");
}, 1000);

console.log("終了");

実行結果

開始
終了
1秒後に実行

コールバック関数と非同期処理

非同期処理では、処理完了後に実行する関数を渡すことがあります。これをコールバック関数と呼びます。

コールバック関数の基本は、[JavaScriptのコールバック関数とは?基本から実践まで徹底解説]で詳しく解説しています。

setTimeout(() => {
  console.log("処理完了");
}, 1000);

この無名関数がコールバック関数です。

非同期処理でよくある問題

非同期処理では、処理が完了する前に結果を使おうとしてしまうことがあります。

let result;

setTimeout(() => {
  result = "完了";
}, 1000);

console.log(result);

実行結果

undefined

setTimeout() の処理がまだ終わっていないためです。

Promiseとは

Promiseは、非同期処理の完了状態を表すオブジェクトです。

状態は3つあります。

  • pending(処理中)
  • fulfilled(成功)
  • rejected(失敗)

Promiseの基本は、[JavaScriptのPromiseとは?非同期処理の基本をわかりやすく解説]で詳しく解説しています。

Promiseの基本例

const promise = new Promise((resolve) => {
  setTimeout(() => {
    resolve("完了しました");
  }, 1000);
});

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

実行結果

完了しました

async/awaitとは

async/await は、Promiseをより読みやすく書くための構文です。

Promiseチェーンでは .then() を連続して記述しますが、async/await を使うと同期処理のように上から順番に書けます。

async/awaitの基本は、[JavaScriptのasync/awaitとは?非同期処理の仕組みを基礎から解説]で詳しく解説しています。

async/awaitの基本例

function wait(ms) {
  return new Promise((resolve) => {
    setTimeout(() => {
      resolve("完了しました");
    }, ms);
  });
}

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

main();

実行結果

完了しました

awaitの役割

await は、Promiseの完了を待ってから次の処理へ進みます。

function wait(ms) {
  return new Promise((resolve) => {
    setTimeout(() => {
      resolve("待機終了");
    }, ms);
  });
}

async function main() {
  console.log("開始");

  const result = await wait(1000);

  console.log(result);
  console.log("終了");
}

main();

実行結果

開始
待機終了
終了

非同期処理のエラー処理

非同期処理ではエラーが発生することがあります。

Promiseでのエラー処理

const promise = new Promise((resolve, reject) => {
  reject(new Error("エラーが発生しました"));
});

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

実行結果

エラーが発生しました

async/awaitでのエラー処理

function fail() {
  return new Promise((resolve, reject) => {
    reject(new Error("エラーが発生しました"));
  });
}

async function main() {
  try {
    await fail();
  } catch (error) {
    console.log(error.message);
  }
}

main();

実行結果

エラーが発生しました

try...catch の基本は、[JavaScriptのtry/catchとは?エラー処理の基本と使い方を徹底解説]で詳しく解説しています。

Promise.all()で複数の非同期処理をまとめて実行する

複数の非同期処理を同時に実行し、すべて完了してから結果を取得したい場合は Promise.all() を使います。

function wait(ms, message) {
  return new Promise((resolve) => {
    setTimeout(() => {
      resolve(message);
    }, ms);
  });
}

async function main() {
  const results = await Promise.all([
    wait(1000, "1つ目"),
    wait(500, "2つ目"),
    wait(1500, "3つ目")
  ]);

  console.log(results);
}

main();

実行結果

["1つ目", "2つ目", "3つ目"]

Promise.all() の詳細は、[JavaScriptのPromise.allとは?複数の非同期処理をまとめて実行する方法]で解説しています。

fetch()と非同期処理

Web APIからデータを取得する fetch() も非同期処理です。

async function main() {
  const response = await fetch("https://jsonplaceholder.typicode.com/todos/1");
  const data = await response.json();

  console.log(data.title);
}

main();

非同期処理の流れ

非同期処理の基本的な流れは次の通りです。

  1. 時間のかかる処理を開始する
  2. 完了を待たずに次の処理へ進む
  3. 完了したらコールバックやPromiseで結果を受け取る

非同期処理とイベントループ

JavaScriptは1つのスレッドで動作しますが、非同期処理によって複数の作業を効率的に扱えます。

この仕組みを支えているのがイベントループです。

イベントループの概要

  1. 同期処理を実行する
  2. 非同期処理を登録する
  3. 同期処理を続ける
  4. 非同期処理が完了すると実行待ちになる
  5. メイン処理が空いたタイミングで実行される

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

順番通りに書いても順番通りに実行されるとは限らない

非同期処理では、コードの記述順と実行順が異なることがあります。

Promiseは非同期処理の結果を表す

成功・失敗・処理中という状態を持ちます。

async/awaitで読みやすく書ける

同期処理のような見た目で記述できます。

非同期処理でよく使う機能

  • setTimeout()
  • setInterval()
  • fetch()
  • Promise
  • Promise.all()
  • async/await

非同期処理でよくある間違い

awaitを使うにはasyncが必要

async function main() {
  await Promise.resolve();
}

main();

Promiseを返さない関数にawaitを使っても意味がない

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

main();

実行結果

123

非同期処理の学習順序

非同期処理は次の順番で学ぶと理解しやすくなります。

  1. コールバック関数
  2. Promise
  3. .then()
  4. .catch()
  5. async/await
  6. Promise.all()

まとめ

JavaScriptの非同期処理とは、時間のかかる処理を待たずに次の処理を進める仕組みです。

  • 同期処理は完了してから次へ進む
  • 非同期処理は完了を待たずに次へ進む
  • setTimeout() は代表的な非同期処理
  • Promiseは非同期処理の結果を表す
  • async/awaitで読みやすく書ける
  • try...catch でエラー処理ができる
  • Promise.all() で複数処理をまとめられる

非同期処理を理解すると、API通信やファイル操作など実践的なJavaScriptを扱えるようになります。

関連記事