「Promiseはなんとなく理解できたけど、async/awaitになると急にわからなくなる」
「awaitを付ける場所を間違えてエラーが出た」
そんな悩みを抱えていませんか。
私自身、TypeScriptでAPI通信や非同期処理を学び始めたとき、Promiseチェーンまでは理解できても、async/awaitを使う場面で何度も手が止まりました。しかし結論から言うと、**async/awaitはPromiseを“わかりやすく書き直した仕組み”**に過ぎません。
この記事では、async/awaitの基本からPromiseとの関係、よくあるつまずきポイントまでを、初心者目線で丁寧に解説します。読み終わる頃には、非同期処理に対する苦手意識がかなり軽くなっているはずです。
async/awaitがわからなくなる理由
async/awaitで混乱する原因は、文法そのものよりも「裏側で何が起きているか」が見えにくい点にあります。
多くの人は次のような流れでつまずきます。
まずPromiseを学び、thenやcatchの使い方を覚えます。その直後にasync/awaitが登場し、「書き方が違うのに、やっていることは同じ」と説明されます。この時点で頭の中が整理できていないと、awaitを魔法のようなものだと誤解してしまいます。
しかし、async/awaitはPromiseを置き換える新しい仕組みではありません。**Promiseの書き方を“同期処理っぽく見せているだけ”**なのです。
async/awaitの正体とは何か
asyncは「この関数はPromiseを返す」という宣言
まず理解すべきポイントは、asyncを付けた関数は必ずPromiseを返すという点です。
async function sample() {
return 10;
}
この関数は、数値10を直接返しているように見えますが、実際には以下と同じ意味になります。
function sample() {
return Promise.resolve(10);
}
つまりasyncは、「この関数の戻り値はPromiseですよ」と明示するためのキーワードだと考えると理解しやすくなります。
awaitは「Promiseが終わるまで待つ」だけ
awaitはPromiseの前でしか使えません。
そしてその役割は非常にシンプルで、Promiseがresolveされるまで処理を止めることです。
const result = await fetchData();
このコードは、fetchData()がPromiseを返し、その結果が確定するまで次の行に進まないという意味になります。
重要なのは、JavaScript全体が止まるわけではないという点です。止まるのは「そのasync関数の中だけ」で、イベントループの仕組み自体は変わっていません。
Promiseとasync/awaitの違いを整理する
Promiseとasync/awaitは対立する概念ではありません。
関係性としては、**Promiseが土台で、async/awaitはその糖衣構文(シンタックスシュガー)**です。
Promiseチェーンでは次のように書いていた処理も、
fetch(url)
.then(res => res.json())
.then(data => console.log(data))
.catch(err => console.error(err));
async/awaitを使うとこう書けます。
try {
const res = await fetch(url);
const data = await res.json();
console.log(data);
} catch (err) {
console.error(err);
}
処理内容は同じですが、async/awaitのほうが上から下へ読むだけで流れが理解できるのが大きなメリットです。
async/awaitでよくあるミス
awaitをasync関数の外で使う
最も多いエラーの一つが、asyncを付けていない関数の中でawaitを使ってしまうケースです。
awaitは必ずasync関数の中でしか使えません。
このエラーが出たら、「この関数は本当に非同期にする必要があるか?」と一度立ち止まるのがコツです。
awaitを付け忘れる
awaitを付け忘れると、Promiseそのものが変数に代入されます。
const data = fetchData(); // Promiseが入る
この状態で中身を使おうとすると、想定外の挙動になります。
「値がまだ来ていない可能性がある処理か?」を考え、Promiseならawaitを付ける意識を持つことが重要です。
TypeScriptとasync/awaitの相性が良い理由
TypeScriptではasync/awaitが特に強力です。
理由は、戻り値の型がPromiseとして明確になるからです。
async function getUser(): Promise<User> {
const res = await fetch("/api/user");
return res.json();
}
このように書くことで、「この関数はUser型を返すPromiseだ」とコードレベルで保証できます。非同期処理が増えても、型が道しるべになり、バグを未然に防げます。
非同期処理を理解するための考え方
async/awaitを理解する近道は、「同期処理っぽく見えるけど、中身は非同期」という視点を持つことです。
- awaitは「結果が出るまで待つ」
- asyncは「Promiseを返す関数になる」
- 実態はPromiseが裏で動いている
この3点を意識するだけで、非同期処理への抵抗感はかなり減ります。
まとめ
async/awaitは、Promiseを難しくするものではなく、理解しやすく書くための道具です。
Promiseの仕組みを知った上でasync/awaitを使えば、非同期処理は一気に読みやすく、保守しやすくなります。
もし今、「非同期処理が苦手」と感じているなら、それは才能の問題ではなく、整理の問題です。
Promise → async → await、この役割を一つずつ分解して理解すれば、必ず乗り越えられます。
次は実際のfetchやAPI通信と組み合わせて使うことで、さらに理解が深まるはずです。

コメント