JavaScript | 非同期処理:async / await – async 関数の定義

JavaScript JavaScript
スポンサーリンク

async / await の「async 関数」を一言でいうと

async 関数は、
「中で await が使える “非同期版の関数”」 です。

先に一番大事なポイントだけ言うと:

  • 関数の前に async を付けると、その関数は 必ず Promise を返す関数になる
  • async 関数の中では、await を使って 非同期処理の完了を “同期っぽく” 待てる

ここが重要です。
async は「なんか非同期っぽい感じになる魔法」ではなく、
「この関数は Promise を返すよ。その中では await を使ってもいいよ」という“約束の宣言” です。
定義の形と、返り値のルールをしっかり押さえましょう。


async 関数の基本的な定義のしかた

function 文に async を付ける

一番素直な形は、通常の関数の前に async を付ける書き方です。

async function fetchData() {
  return "hello";
}
JavaScript

このとき fetchData() は、
実際には "hello" ではなく「"hello" を解決する Promise」を返します。

const result = fetchData();

console.log(result); // Promise { <fulfilled>: "hello" } のようなもの
JavaScript

async を付けた瞬間、その関数の返り値は
「生の値」→「Promise に包まれた値」
に変わる、と思ってください。

関数式・アロー関数でも同じ

関数式にも async を付けられます。

const fetchData = async function () {
  return "hello";
};
JavaScript

アロー関数の場合も同様です。

const fetchData = async () => {
  return "hello";
};
JavaScript

どれも意味は同じで、
「呼び出すと Promise を返す関数」 を定義しているだけです。

ここが重要です。
async は「関数の中身」ではなく
「その関数の“型”」を変えます。
どの書き方であっても、async を付けたら「Promise を返す関数」になる
と覚えてください。


async 関数の返り値のルール(ここをちゃんと深掘る)

return した値は Promise に自動で包まれる

async 関数の中で return した値は、
自動的に Promise.resolve(値) された形になります。

async function getNumber() {
  return 42;
}

getNumber().then(value => {
  console.log(value); // 42
});
JavaScript

getNumber()42 ではなく Promise<number> を返しますが、
then の中では普通に 42 が取れます。

例:普通の関数との違い

普通の関数:

function normal() {
  return 42;
}

const v1 = normal(); // v1 は 42(number)
JavaScript

async 関数:

async function asyncFunc() {
  return 42;
}

const v2 = asyncFunc(); // v2 は Promise(中身は 42)
JavaScript

見た目は似ていますが、
呼び出した結果の「型」が根本的に違う ことに注意してください。

何も return しない場合

async 関数で何も返さない(return しない)場合、
undefined を解決する Promise になります。

async function doSomething() {
  console.log("処理中...");
}

doSomething().then(value => {
  console.log("then の value:", value); // undefined
});
JavaScript

ここが重要です。
async 関数の返り値は「Promise である」という約束が絶対です。
return した値は必ず Promise に包まれ、
何も return しなくても Promise<void> 的なものが返ります。
この “Promise を返す関数” だからこそ、await で待てるようになります。


async 関数内での await のイメージ

await で「Promise の結果を待ってから次へ進む」

async 関数の中では、await を使って
Promise が完了するまで「そこで一旦止まり」、
結果を普通の値として受け取れます。

例えば、fetch(HTTPリクエスト)を使う例:

async function fetchJson(url) {
  const response = await fetch(url);     // ここでレスポンスを待つ
  const data = await response.json();    // JSON 変換を待つ
  return data;                           // data を解決する Promise が返る
}
JavaScript

この fetchJson

  • 呼ぶ側から見ると:「Promise を返す関数」
  • 中の書き方は:「一見同期っぽく、上から順に進むコード」

になっています。

await が使えるのは async 関数の中だけです。
(トップレベル await が使える環境もありますが、まずは「async の中で使う」と覚えれば十分です)

ここが重要です。
async で関数を定義する本当の意味は、「この関数の中では await で Promise を“待てる” ようにすること」。
外から見れば Promise を返す関数、
中から見れば “同期っぽく書ける非同期関数”。
この二重構造をイメージできると、一気に理解が進みます。


例題:async 関数の定義と使い方を具体的に見る

例1:1 秒待ってから値を返す async 関数

まず、1 秒後に解決される Promise を返す関数を作ります。

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

これを await で使う async 関数を定義します。

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

run() を呼ぶと、
「開始 → (1秒後)→ 1秒経ちました → 終了」
という順でログが出ます。

run 自体は Promise を返す関数です。

const p = run();
console.log(p); // Promise { <pending> } のようなもの
JavaScript

例2:async 関数の定義と then の両方を組み合わせる

runawait する側がさらに別の async 関数だとしたら:

async function main() {
  await run();           // run の完了を待つ
  console.log("main 完了");
}
JavaScript

あるいは Promise として扱っても構いません。

run().then(() => {
  console.log("run が終わったので then");
});
JavaScript

ここが重要です。
async 関数は「中では await が使える」けれど、
「外から見ればただの Promise を返す関数」です。
だから、await asyncFunc() と書いてもいいし、
asyncFunc().then(...) と書いてもいい。
Promise の世界と async/await の世界は、同じ土台の上に立っています。


async 関数のエラーハンドリング(定義とセットで意識してほしい)

throw したエラーは Promise の reject になる

async 関数の中で throw すると、
その関数の Promise は reject されます。

async function failExample() {
  throw new Error("何かがうまくいかなかった");
}

failExample()
  .then(() => {
    console.log("ここは実行されない");
  })
  .catch(err => {
    console.log("エラー:", err.message); // "エラー: 何かがうまくいかなかった"
  });
JavaScript

await 側では try / catch で受け止める

await を使う側では、
通常の同期コードと同じように try / catch でエラーを扱えます。

async function main() {
  try {
    await failExample();
    console.log("ここは実行されない");
  } catch (err) {
    console.log("catch で受け止めた:", err.message);
  }
}
JavaScript

ここが重要です。
async 関数の定義をしたら、
「この関数が失敗することはあるか? そのとき何を throw するか?」までセットで設計するのがとても大事です。
外から見れば、「resolve か reject か」の Promise になるので、
成功値・失敗値の形をきちんと決めておくことが、後々の読みやすさと扱いやすさにつながります。


初心者として async 関数の定義で本当に押さえてほしいこと

async を関数の前に付けると、その関数は必ず Promise を返す。
return 値 は、内部的には Promise.resolve(値) になる。

async 関数の中でだけ await が使える。
await promise は、Promise の完了を待って「その結果の値」を返す。

何も return しない async 関数も、
外から見れば Promise<void> 的なもの(中身は undefined)を返している。

async 関数内で throw したエラーは、その Promise の reject になる。
await する側は try / catch で同期コードのようにエラー処理ができる。

ここが重要です。
async 関数の定義は、
「この処理は非同期だよ。だから Promise を返すよ。その中では await で綺麗に書けるよ」という宣言です。
“Promise を返す関数” を自分で定義できるようになると、
非同期処理を「中身はきれいな手続き」「外側は Promise」として整理できるようになって、
コード全体の見通しが一気に良くなります。

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

// 1. 2 秒待ってから "done" を返す async 関数 delayDone を定義し、
//    それを await する async 関数 main を作って、結果を console.log する。

// 2. Promise を直接 new で作る関数と、
//    同じことをする async 関数をそれぞれ書いて、
//    「外から見たときにどちらも Promise である」ことを確かめてみる。
JavaScript

「async の有無で何が変わるのか」を、
自分の手で書いて・ログを見て・体で覚えていくと、
非同期処理の感覚がぐっと掴みやすくなります。

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