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 されたのと同じになる。

awaitasync 関数の中でしか使えない。
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をコピーしました