JavaScriptで始める非同期処理入門
JavaScriptで始める非同期処理入門
JavaScriptでウェブアプリケーションを開発していると、必ず出会うのが非同期処理です。APIからデータを取得したり、ファイルを読み込んだり、タイマー処理を実装したりと、様々な場面で非同期処理が必要になります。
この記事では、JavaScriptにおける非同期処理の基本を理解し、実践的なコードで学んでいきましょう。
なぜ非同期処理が必要か?
JavaScriptは基本的にシングルスレッドで動作します。これは一度に1つの処理しか実行できないことを意味します。
例えば、サーバーからデータを取得する処理が3秒かかるとしましょう。この間、同期的に処理を行うと、ユーザーインターフェースが完全に固まってしまいます。ボタンをクリックしても、スクロールしても反応しない状態になってしまいます。
非同期処理を利用することで、データ取得中もUIの操作を可能にし、ユーザー体験を向上させることができます。
非同期処理の基本:コールバック関数
非同期処理の最も古典的な方法はコールバック関数です。処理が完了したら実行する関数を引数として渡します。
// setTimeout を使った例
console.log("処理開始");
setTimeout(() => {
console.log("3秒後に実行されました");
}, 3000);
console.log("処理は続行しています");
実行結果:
処理開始
処理は続行しています
3秒後に実行されました
しかし、コールバック関数を多用すると「コールバック地獄」と呼ばれる可読性の低いコードになりがちです。
getData(function(a) {
getMoreData(a, function(b) {
getEvenMoreData(b, function(c) {
getEvenEvenMoreData(c, function(d) {
getFinalData(d, function(final) {
console.log(final);
});
});
});
});
});
モダンな非同期処理:Promise
ES6から導入されたPromiseは、非同期処理をより扱いやすくするための仕組みです。
// Promiseの基本的な使い方
const myPromise = new Promise((resolve, reject) => {
// 非同期処理を実行
const success = true;
if (success) {
resolve("成功しました!"); // 処理成功
} else {
reject("エラーが発生しました"); // 処理失敗
}
});
// Promiseを使用する
myPromise
.then(result => {
console.log(result); // "成功しました!"
})
.catch(error => {
console.error(error);
});
Promiseを使うことで、コールバック地獄も解消できます。
getData()
.then(a => getMoreData(a))
.then(b => getEvenMoreData(b))
.then(c => getEvenEvenMoreData(c))
.then(d => getFinalData(d))
.then(final => {
console.log(final);
})
.catch(error => {
console.error("エラーが発生しました:", error);
});
さらに進化した非同期処理:async/await
ES2017で導入されたasync/awaitは、Promiseをさらに扱いやすくする構文です。非同期処理を同期処理のように書けるため、コードの可読性が大幅に向上します。
async function fetchData() {
try {
const a = await getData();
const b = await getMoreData(a);
const c = await getEvenMoreData(b);
const d = await getEvenEvenMoreData(c);
const final = await getFinalData(d);
console.log(final);
return final;
} catch (error) {
console.error("エラーが発生しました:", error);
}
}
// 関数を実行
fetchData();
実践的な例:Fetch APIでデータを取得する
最後に、実際のウェブ開発でよく使うFetch APIを使った非同期データ取得の例を見てみましょう。
// Promise を使った例
function fetchUserData(userId) {
return fetch(`https://api.example.com/users/${userId}`)
.then(response => {
if (!response.ok) {
throw new Error('ネットワークエラーが発生しました');
}
return response.json();
});
}
fetchUserData(123)
.then(userData => {
console.log(userData);
})
.catch(error => {
console.error('データの取得に失敗しました:', error);
});
// async/await を使った例
async function fetchUserData(userId) {
try {
const response = await fetch(`https://api.example.com/users/${userId}`);
if (!response.ok) {
throw new Error('ネットワークエラーが発生しました');
}
const userData = await response.json();
return userData;
} catch (error) {
console.error('データの取得に失敗しました:', error);
throw error;
}
}
// 使用例
async function displayUserInfo() {
try {
const user = await fetchUserData(123);
console.log(user);
} catch (error) {
// エラー処理
}
}
まとめ
JavaScriptの非同期処理は、以下のように進化してきました:
- コールバック関数: 古典的だが、ネストが深くなりがち
- Promise: 連鎖的な処理が書きやすく、エラーハンドリングも改善
- async/await: 同期コードのような書き方で可読性が向上
どの方法も状況によって使い分けることが大切です。特に最近のプロジェクトでは、async/awaitを基本としつつ、Promise.allなどのPromiseメソッドと組み合わせて使うのが一般的です。
非同期処理を理解し、適切に使いこなせるようになれば、より快適なユーザー体験を提供できるウェブアプリケーションが開発できるようになるでしょう。
Discussion