async/awaitとは?日常例で理解する非同期処理の基本
こんにちは、とまだです。
みなさん、JavaScriptで非同期処理を書くとき、「なんだか複雑だな」と感じたことはありませんか?
今回は現役のエンジニア、そして元プログラミングスクール講師としての経験から、async/awaitについて解説します。
非同期処理の必要性
なぜ非同期処理が必要なのか
JavaScriptで外部APIを呼び出すとき。
データの取得に時間がかかりますよね。
もし、この待ち時間で画面が固まったら?
ユーザーは困ってしまいます。
そこで登場するのが非同期処理です。
日常生活で例えると
非同期処理は料理に似ています。
ご飯を炊きながら、おかずも作る。
同時に複数の作業を進められます。
一方、同期処理は一列に並ぶ自動販売機。
前の人の購入が終わるまで待つ必要があります。
コールバックからPromiseへの進化
コールバック地獄の問題
以前はコールバック関数を使っていました。
でも、処理が増えるとネストが深くなります。
getData(function(result1) {
processData(result1, function(result2) {
saveData(result2, function(result3) {
// さらに続く...
});
});
});
このコードは読みづらいですよね。
エラー処理も複雑になってしまいます。
Promiseで改善
Promiseの登場で、処理が連鎖的に書けるようになりました。
getData()
.then(result1 => processData(result1))
.then(result2 => saveData(result2))
.catch(error => console.error(error));
コールバックよりは読みやすくなりました。
でも、まだ完璧ではありません。
async/awaitの基本
async関数の定義
関数の前にasyncを付けるだけです。
async function fetchUserData() {
// 非同期処理を書く
}
これで非同期関数の完成です。
中でawaitが使えるようになります。
awaitの使い方
awaitはPromiseの結果を待ちます。
async function getUserInfo() {
const response = await fetch('/api/user');
const data = await response.json();
return data;
}
見た目は同期的なコードと同じです。
でも実際は非同期で動いています。
実務での活用例
API通信の実装
実際のWebアプリケーションでよく使う例です。
async function fetchWeatherData(city) {
try {
const response = await fetch(`/api/weather?city=${city}`);
if (!response.ok) {
throw new Error('天気データの取得に失敗しました');
}
const weatherData = await response.json();
return weatherData;
} catch (error) {
console.error('エラーが発生しました:', error);
throw error;
}
}
エラーハンドリングも含めて、シンプルに書けます。
実務では、このようなAPI通信が頻繁に発生します。
複数の非同期処理
複数のAPIを同時に呼ぶこともあります。
async function loadDashboardData() {
try {
// 並列で実行
const [users, posts, comments] = await Promise.all([
fetch('/api/users').then(res => res.json()),
fetch('/api/posts').then(res => res.json()),
fetch('/api/comments').then(res => res.json())
]);
return { users, posts, comments };
} catch (error) {
console.error('データ取得エラー:', error);
}
}
Promise.allを使えば、並列処理も簡単です。
すべての処理が完了するまで待ちます。
よくある疑問と注意点
エラーハンドリングの方法
try-catchを使ってエラーを捕捉します。
async function safeApiCall() {
try {
const result = await riskyOperation();
return result;
} catch (error) {
// エラー時の処理
console.error('処理に失敗しました:', error.message);
// デフォルト値を返す
return null;
}
}
エラーが起きても、アプリが止まりません。
適切な処理を続けられます。
forループ内でのawait
ループ内でawaitを使う際は注意が必要です。
// 順番に処理(遅い)
async function processSequentially(items) {
for (const item of items) {
await processItem(item);
}
}
// 並列に処理(速い)
async function processInParallel(items) {
await Promise.all(
items.map(item => processItem(item))
);
}
状況に応じて使い分けましょう。
並列処理の方が高速ですが、APIの制限に注意です。
async関数の戻り値
async関数は必ずPromiseを返します。
async function getValue() {
return 42;
}
// 呼び出し側
getValue().then(value => {
console.log(value); // 42
});
// またはasync関数内で
async function main() {
const value = await getValue();
console.log(value); // 42
}
この仕様を理解しておくことが大切です。
混乱を避けられます。
まとめ
async/awaitは非同期処理を簡潔に書ける構文です。
コールバックやPromiseより読みやすく、メンテナンスしやすいコードが書けます。
エラーハンドリングもtry-catchで直感的に。
並列処理もPromise.allで実現できます。
初心者の方も、基本的な使い方から始めて。
徐々に応用的な使い方を覚えていけば大丈夫です。
実際のプロジェクトで使いながら、理解を深めていってください。
著者について

とまだ
フルスタックエンジニア
Learning Next の創設者。Ruby on Rails と React を中心に、プログラミング教育に情熱を注いでいます。初心者が楽しく学べる環境作りを目指しています。
著者の詳細を見る →