1. そもそも「非同期処理」って何?
JavaScriptは基本的に 1つの処理を順番に実行する(同期的) 言語です。
ただし、通信(API)・タイマー・ファイル読み込みなど 時間がかかる処理 を「待っている間」に他の処理が止まると困ります。
そこで登場するのが 非同期処理です。
- 同期:終わるまで次に進まない
- 非同期:終わるのを待たずに次に進む(終わったら通知してくる)
2. 非同期っぽさを体験する(setTimeout)
まずは最小の例です。
console.log("A");
setTimeout(() => {
console.log("B(あとから)");
}, 1000);
console.log("C");
出力はこうなります:
A
C
B(あとから)setTimeout は「1秒後に実行予約」するだけなので、処理は止まらず C が先に出ます。
3. Callback(コールバック)で非同期を扱う
Callbackは「終わったら呼ぶ関数」を渡すスタイルです。
例:疑似API(成功/失敗あり)
Zfunction fetchUserCallback(userId, callback) {
setTimeout(() => {
if (!userId) {
callback(new Error("userIdがありません"), null);
return;
}
callback(null, { id: userId, name: "Taro" });
}, 500);
}
fetchUserCallback(1, (err, user) => {
if (err) {
console.error("失敗:", err.message);
return;
}
console.log("成功:", user);
});Callbackのつらさ:ネストが深くなりがち(コールバック地獄)
fetchUserCallback(1, (err, user) => {
if (err) return console.error(err);
fetchUserCallback(user.id + 1, (err2, user2) => {
if (err2) return console.error(err2);
fetchUserCallback(user2.id + 1, (err3, user3) => {
if (err3) return console.error(err3);
console.log("3人取得:", user, user2, user3);
});
});
});問題点
- ネストが増えて読みにくい
- エラーハンドリングが散らばる
- 処理の流れを追いづらい
4. Promiseで改善する
Promiseは「未来に結果が返ってくる」ことを表現する仕組みです。
- resolve:成功
- reject:失敗
Callback版をPromise版にする
function fetchUserPromise(userId) {
return new Promise((resolve, reject) => {
setTimeout(() => {
if (!userId) {
reject(new Error("userIdがありません"));
return;
}
resolve({ id: userId, name: "Taro" });
}, 500);
});
}then/catchでつなげられる
fetchUserPromise(1)
.then((user) => {
console.log("1人目:", user);
return fetchUserPromise(user.id + 1);
})
.then((user2) => {
console.log("2人目:", user2);
return fetchUserPromise(user2.id + 1);
})
.then((user3) => {
console.log("3人目:", user3);
})
.catch((err) => {
console.error("どこかで失敗:", err.message);
});Callbackより良い点
- ネストが浅くなる
- エラーは最後にまとめてcatchできる
- “処理をつなぐ”のが自然
5. async/awaitでさらに読みやすくする
async/await は Promise を 同期処理っぽい見た目で書ける構文です。
- async を付けた関数は必ずPromiseを返す
- await は Promise の完了を待って結果を取り出す
Promiseをasync/awaitで書き換え
async function main() {
try {
const user1 = await fetchUserPromise(1);
console.log("1人目:", user1);
const user2 = await fetchUserPromise(user1.id + 1);
console.log("2人目:", user2);
const user3 = await fetchUserPromise(user2.id + 1);
console.log("3人目:", user3);
} catch (err) {
console.error("失敗:", err.message);
}
}
main();一気に読みやすくなるポイント
- 上から下へ順番に読める
- try/catchでエラー処理をまとめられる
- ロジックが「普通の手続き処理」に近い
まとめ
非同期処理は「時間がかかる処理を待つ/待たない」を扱う仕組みです。
- Callback:動くけどネストが増えがち
- Promise:チェーンで書けて見通しが良い
- async/await:Promiseを同期っぽく書けて最も読みやすい
