10. 非同期処理(コールバック、プロミス、async/await)
JavaScript はシングルスレッドで動作しますが、非同期処理を利用することで、時間のかかる操作を効率的に扱うことができます。この章では、JavaScript における非同期処理の基本として、コールバック、プロミス、そして最新の async/await
を紹介します。
10.1 コールバック
10.1.1 コールバックの基本
コールバック関数は、他の関数に引数として渡され、他の関数の実行が完了した後に呼び出される関数です。例えば、以下のように使います:
function fetchData(callback) {
setTimeout(() => {
// データを取得したと仮定します
const data = { user: "John Doe" };
callback(data);
}, 1000); // 1秒後に実行
}
function handleData(data) {
console.log("データを受け取りました:", data);
}
fetchData(handleData);
10.1.2 コールバック地獄
複雑な非同期操作をコールバックで連続して処理することは、可読性が低く、エラーハンドリングも難しくなります。これは「コールバック地獄」と呼ばれます。
function firstTask(callback) {
setTimeout(() => {
console.log("First task completed");
callback();
}, 1000);
}
function secondTask(callback) {
setTimeout(() => {
console.log("Second task completed");
callback();
}, 1000);
}
function thirdTask(callback) {
setTimeout(() => {
console.log("Third task completed");
callback();
}, 1000);
}
firstTask(() => {
secondTask(() => {
thirdTask(() => {
console.log("All tasks completed");
});
});
});
10.2 プロミス
10.2.1 プロミスの基本
プロミス(Promise)は、非同期操作の結果を扱うオブジェクトで、3 つの状態を持ちます:pending
(保留中)、fulfilled
(解決済み)、rejected
(拒否済み)。以下はプロミスを使った例です:
function fetchData() {
return new Promise((resolve, reject) => {
setTimeout(() => {
const success = true; // 成功したかどうかを仮定
if (success) {
resolve({ user: "John Doe" }); // 成功した場合のデータ
} else {
reject("データ取得に失敗しました"); // エラーメッセージ
}
}, 1000);
});
}
fetchData()
.then((data) => {
console.log("データを受け取りました:", data);
})
.catch((error) => {
console.error("エラーが発生しました:", error);
});
10.2.2 チェーンによる連続処理
プロミスはチェーン可能で、連続的な非同期処理を簡潔に書くことができます。
function firstTask() {
return new Promise((resolve) => {
setTimeout(() => {
console.log("First task completed");
resolve();
}, 1000);
});
}
function secondTask() {
return new Promise((resolve) => {
setTimeout(() => {
console.log("Second task completed");
resolve();
}, 1000);
});
}
function thirdTask() {
return new Promise((resolve) => {
setTimeout(() => {
console.log("Third task completed");
resolve();
}, 1000);
});
}
firstTask()
.then(secondTask)
.then(thirdTask)
.then(() => {
console.log("All tasks completed");
});
10.3 async/await
10.3.1 async/await の基本
async
と await
は、プロミスをさらに簡潔に記述できるようにする構文です。async
関数は常にプロミスを返し、await
式はプロミスの結果が解決されるまで待ちます。
async function fetchData() {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve({ user: "John Doe" });
}, 1000);
});
}
async function getData() {
try {
const data = await fetchData();
console.log("データを受け取りました:", data);
} catch (error) {
console.error("エラーが発生しました:", error);
}
}
getData();
10.3.2 複数の非同期処理の直列実行
async/await
を使うことで、複数の非同期操作をシンプルに直列実行できます。
async function firstTask() {
return new Promise((resolve) => {
setTimeout(() => {
console.log("First task completed");
resolve();
}, 1000);
});
}
async function secondTask() {
return new Promise((resolve) => {
setTimeout(() => {
console.log("Second task completed");
resolve();
}, 1000);
});
}
async function thirdTask() {
return new Promise((resolve) => {
setTimeout(() => {
console.log("Third task completed");
resolve();
}, 1000);
});
}
async function runTasks() {
await firstTask();
await secondTask();
await thirdTask();
console.log("All tasks completed");
}
runTasks();
10.4 総括
この章では、JavaScript の非同期処理の基礎として、コールバック、プロミス、そして async/await
を紹介しました。これらの技術を使い分けることで、効率的かつ可読性の高いコードを書くことができます。実際にコードを書いて試してみてください。