JavaScript | 非同期処理:async / await – await の役割

JavaScript JavaScript
スポンサーリンク
スポンサーリンク

await を一言でいうと

await は、
「Promise の完了を“その行で一旦止まって待ち”、結果の値を取り出すためのキーワード」 です。

await promise と書くと、

  • その行で「promise が終わるまで」一旦ストップし
  • resolve された値を、その場で普通の値として受け取れます

ここが重要です。
await は「処理全体を止める」のではなく、
「その async 関数の“続きを一時停止”して、Promise の完了を待つ」 ものです。
非同期処理を、同期処理のように「上から順番に」書けるようにしてくれます。


await の前提:Promise と async 関数

await は「Promise に対して」使う

await の相手は、基本的に Promise です。

const value = await somePromise;
JavaScript

somePromise が

new Promise((resolve, reject) => { ... })
JavaScript

のような Promise であれば、
その Promise が resolve されるまで待ち、
value には resolve 時の値が入ります。

await は async 関数の中でしか使えない

await は、原則として async 関数の中でしか使えません。

async function run() {
  const value = await somePromise;
  console.log(value);
}
JavaScript

関数の前に async を付けるのは、
「この中で await を使っていいよ」という宣言 だと思ってください。

ここが重要です。
await は、「Promise を待つ」+「その関数を一旦止める」ためのスイッチ。
そのスイッチを使える場所が async 関数の中、相手が Promise。
この 2 つの前提は必ずセットで思い出してください。


await の基本動作を具体例で見る

例1:1秒待ってから値を返す Promise を await する

まず、1秒後に解決される Promise を返す関数を用意します。

function wait1sec() {
  return new Promise(resolve => {
    setTimeout(() => {
      resolve("1秒経ちました");
    }, 1000);
  });
}
JavaScript

これを await してみます。

async function run() {
  console.log("開始");
  const message = await wait1sec();  // ここで 1 秒待つ
  console.log(message);              // "1秒経ちました"
  console.log("終了");
}

run();
JavaScript

処理の流れはこうです。

  1. run() が呼ばれる
  2. "開始" を表示
  3. await wait1sec() に到達し、Promise が解決するまで run の「続き」を一時停止
  4. 1 秒後に wait1sec の Promise が resolve される
  5. message に "1秒経ちました" が入り、処理が再開
  6. "1秒経ちました" → "終了" の順で表示

重要なのは、「止まるのは run 関数の中だけ」であって、
ブラウザ全体がフリーズするわけではない
ということです。

例2:値を「取り出す」イメージ

Promise を then で書くとこうです。

wait1sec().then(message => {
  console.log(message);
});
JavaScript

同じことを await で書くと、

const message = await wait1sec();
console.log(message);
JavaScript

となります。

ここが重要です。
await は、「then の中に入っていく」のではなく、
“Promise の中から値を取り出して変数に代入する” イメージです。
これのおかげで、「非同期の結果を使う処理」が、普通の同期コードと同じように書けるようになります。


await を使うと何がうれしいのか

ネストが浅くなり、同期っぽい順序で書ける

Promise を then チェーンで書くとこうなります。

fetch(url)
  .then(response => response.json())
  .then(data => {
    console.log("データ:", data);
  })
  .catch(err => {
    console.error("エラー:", err);
  });
JavaScript

同じことを async / await で書くと:

async function fetchJson(url) {
  try {
    const response = await fetch(url);
    const data = await response.json();
    console.log("データ:", data);
  } catch (err) {
    console.error("エラー:", err);
  }
}
JavaScript

「1. fetch して → 2. JSON にして → 3. ログに出す」という順番が、
コードの見た目と素直に一致 します。

直感的な「上から順に実行」が保てる

例えば、3回連続で何かを待ちたい場合。

async function run() {
  console.log("1つ目");
  await wait1sec();

  console.log("2つ目");
  await wait1sec();

  console.log("3つ目");
  await wait1sec();

  console.log("完了");
}
JavaScript

コードを上から読むだけで、動きのイメージがそのまま掴めます。

ここが重要です。
await の一番のメリットは、「時間の流れ」をコードの読み順と揃えられること。
「まずこれを待って、そのあとこれをやって…」という処理を、
人間の感覚に近い順番で書けるようにしてくれます。


await とエラーハンドリング(try / catch)

Promise 版:catch でエラー処理

Promise だけで書くと、

someAsync()
  .then(result => {
    console.log("成功:", result);
  })
  .catch(err => {
    console.error("エラー:", err);
  });
JavaScript

await 版:try / catch で同期コードライクに

await を使うと、
同期コードと同じように try / catch で書けます。

async function run() {
  try {
    const result = await someAsync();
    console.log("成功:", result);
  } catch (err) {
    console.error("エラー:", err);
  }
}
JavaScript

さらに、複数の await にまたがってまとめてエラーを捕まえられます。

async function run() {
  try {
    const a = await asyncA();
    const b = await asyncB(a);
    const c = await asyncC(b);
    console.log("全部成功:", a, b, c);
  } catch (err) {
    console.error("どこかでエラー:", err);
  }
}
JavaScript

ここが重要です。
await を使うと、「成功の流れ」も「エラーの流れ」も、
同期コードとほぼ同じ感覚で書けるようになります。
非同期特有の「then の中の catch の中の…」というネスト地獄から解放されます。


await が返すものと、await し忘れたときの違い

await すると「中身の値」が取れる

Promise をそのまま代入すると:

const dataPromise = fetchJson(url); // Promise
JavaScript

await で待つと:

const data = await fetchJson(url); // 実際のデータ
JavaScript

dataPromise は「そのうちデータが入る箱」のようなもの。
data は「箱から取り出された中身」です。

await を付け忘れると「思ったより早く進む」

よくあるミスのパターン:

async function run() {
  const data = fetchJson(url); // await を付け忘れた
  console.log(data); // Promise が表示される
}
JavaScript

await を付け忘れると、
Promise が解決される前に、次の行へ進んでしまいます。

async function run() {
  const data = await fetchJson(url); // ここでちゃんと待つ
  console.log(data); // 実際のデータ
}
JavaScript

ここが重要です。
「その場で結果を使いたい」なら必ず await。
「あとで then で処理したい」なら await なしで Promise のまま持っておく。
「箱のまま扱うのか」「中身を取り出すのか」を意識して使い分けてください。


await と並列実行(あえて await しないテクニック)

直列実行:1つずつ順番に待つ

async function run() {
  const a = await taskA();
  const b = await taskB();
  console.log(a, b);
}
JavaScript

taskA が終わってから taskB が始まるので、
A と B は「直列」です。

並列実行:先に Promise を貯めて、まとめて await

async function run() {
  const promiseA = taskA(); // ここで開始
  const promiseB = taskB(); // ここでもう開始

  const a = await promiseA;
  const b = await promiseB;
  console.log(a, b);
}
JavaScript

こうすると、
A と B がほぼ同時に進み、
両方の完了をそれぞれ await で待つ形になります。

Promise.all と await の組み合わせ

もっと分かりやすいのは Promise.all です。

async function run() {
  const [a, b] = await Promise.all([taskA(), taskB()]);
  console.log(a, b);
}
JavaScript

ここが重要です。
await は「その場で待つ」ので、付ける場所しだいで
“順番に実行” にも “並列に実行してからまとめて待つ” にもなります。
「これは順番が大事か?」「同時に進めていいか?」を考えて、
どこで await するかを設計するのが、非同期処理の腕の見せ所です。


await とエラー:Promise の reject がどう見えるか

reject された Promise を await すると throw になる

function failAfter1sec() {
  return new Promise((_, reject) => {
    setTimeout(() => {
      reject(new Error("失敗しました"));
    }, 1000);
  });
}

async function run() {
  try {
    const result = await failAfter1sec(); // ここで throw される
    console.log("成功:", result);
  } catch (err) {
    console.log("エラーを捕まえた:", err.message);
  }
}
JavaScript

await failAfter1sec() のところで、
内部的には「throw された」のと同じ扱いになります。
だから try / catch で受け止められます。

then / catch と await / try の対応関係を意識する

// Promise 版
someAsync()
  .then(result => { ... })
  .catch(err => { ... });

// async / await 版
try {
  const result = await someAsync();
  // ...
} catch (err) {
  // ...
}
JavaScript

この対応をしっかり頭に入れておくと、
Promise ベースのコードを async / await に“翻訳”しやすくなります。

ここが重要です。
await は「resolve された値を取り出す」だけでなく、
「reject されたら throw として扱う」という二面性を持っています。
成功も失敗も、同期コードと同じ感覚で書けるようにしてくれるのが、await の本当の強みです。


初心者として await の役割で本当に押さえてほしいこと

await promise は、「promise が終わるまでその行で待ち、resolve された値を取り出す」。
エラー(reject)のときは、その場で throw されたのと同じになる。

await は async 関数の中でしか使えない。
async 関数は「Promise を返す関数」であり、その中だけで await による“一時停止と再開” が起こる。

await を使うと、「非同期処理の順番」を普通の同期コードと同じような見た目で書ける。
try / catch と組み合わせると、then / catch のネストよりずっと読みやすいエラーハンドリングになる。

await を付ける場所しだいで、「直列に一つずつ待つ」のか、「並列に走らせてからまとめて待つ」のかをコントロールできる。

ここが重要です。
await は「Promise を待つための一時停止ボタン」であり、
「Promise の中身をその場で取り出すための道具」です。
コードを書きながら、
“ここでは箱(Promise)のまま扱いたいのか?”
“それとも中身を取り出して次の処理に渡したいのか?”
を意識して、await を置く場所を決めていくと、非同期処理が一気に自分のコントロール下に入ってきます。

最後に、練習として次の 2 つを自分で書いてみてください。

// 1. 1秒後に "A"、さらに1秒後に "B"、さらに1秒後に "C" を順番に表示する async 関数を、await を使って書いてみる。

// 2. A・B・C を「同時に」1秒後に返す Promise にして、
//    Promise.all と await を使って、「全部そろったらまとめて表示」する関数を書いてみる。
JavaScript

「どこで await するか」を自分なりに試行錯誤してみることが、
await の“本当の役割”を体で理解する一番の近道になります。

タイトルとURLをコピーしました