JavaScript Promiseエラーの原因と解決方法|初心者向け完全ガイド
JavaScriptでプログラミングをしていると、Promiseに関するエラーに遭遇することは多くあります。特に非同期処理を扱う際に、適切なエラーハンドリングができていないと、デバッグが困難になり、予期しない挙動につながります。この記事では、JavaScriptのPromiseエラーの原因から解決方法まで、初心者でもわかりやすいように詳しく解説します。
Promiseエラーの原因
JavaScriptのPromiseエラーが発生する主な原因を理解することが、問題解決の第一歩です。以下に代表的な原因をまとめました。
1. 未処理の拒否(Unhandled Rejection)
最も一般的なPromiseエラーは、catchメソッドを使用せずにPromiseチェーンを終了させることです。Promiseが拒否された場合、catchで処理されないと、エラーが未処理のままになります。これにより、ブラウザやNode.jsが警告を出し、予期しない挙動につながる可能性があります。
2. エラーハンドリングの欠落
thenメソッドを使用する際に、エラーコールバックを指定しないケースが多くあります。thenの第二引数にエラーハンドラーを指定しないと、エラーが適切に処理されません。
3. asyncとawaitの不正確な使用
async/awaitを使用する際に、try-catchブロックがない場合、エラーが捕捉されず、関数全体が拒否されます。
4. Promiseチェーンの中断
Promiseチェーンの途中で不正な値を返す、または値を返さずにundefinedになるなど、チェーンの流れが予期しない方向に進むことがあります。
Promiseエラーの解決手順
ステップ1: catchメソッドの追加
最初のステップは、すべてのPromiseチェーンにcatchメソッドを追加することです。catchメソッドはPromiseが拒否された場合に実行されます。
ステップ2: エラーログの出力
エラーが発生した場合、console.errorを使用してエラー情報をログに出力します。これによって、本番環境でのデバッグが容易になります。
ステップ3: ユーザーへのフィードバック
エラーが発生した場合、ユーザーに対して適切なメッセージを表示することが重要です。単にエラーを隠すのではなく、ユーザーが状況を理解できるようにしましょう。
ステップ4: エラーのリスロー
場合によっては、エラーを処理した後に、別のcatchで処理するためにエラーを再度スローすることが必要です。
Promiseエラーの解決コード例
基本的なcatchの使用方法
// 基本的なPromiseチェーンでのエラーハンドリング
fetch('https://api.example.com/data')
.then(response => response.json())
.then(data => {
console.log('データ取得成功:', data);
})
.catch(error => {
console.error('エラーが発生しました:', error);
});
このコード例では、fetchでAPI呼び出しを行い、成功時はデータを処理し、失敗時はcatchでエラーを処理しています。
thenメソッドでのエラーハンドリング
// thenメソッドの第二引数でエラーを処理
fetch('https://api.example.com/data')
.then(
response => response.json(),
error => {
console.error('ネットワークエラー:', error);
throw error; // エラーを次のcatchに渡す
}
)
.then(data => {
console.log('データ処理:', data);
})
.catch(error => {
console.error('最終的なエラー処理:', error);
});
async/awaitでのエラーハンドリング
// async/awaitとtry-catchを使用したエラーハンドリング
async function fetchData() {
try {
const response = await fetch('https://api.example.com/data');
if (!response.ok) {
throw new Error(`HTTPエラー! ステータス: ${response.status}`);
}
const data = await response.json();
console.log('データ取得成功:', data);
return data;
} catch (error) {
console.error('エラーが発生しました:', error);
// ユーザーに対して適切なメッセージを表示
alert('データの取得に失敗しました。しばらくしてから再度お試しください。');
}
}
fetchData();
複数のPromiseの並列処理でのエラーハンドリング
// Promise.allでのエラーハンドリング
const promise1 = fetch('https://api.example.com/data1').then(r => r.json());
const promise2 = fetch('https://api.example.com/data2').then(r => r.json());
const promise3 = fetch('https://api.example.com/data3').then(r => r.json());
Promise.all([promise1, promise2, promise3])
.then(results => {
console.log('すべてのデータを取得しました:', results);
})
.catch(error => {
console.error('いずれかのPromiseが失敗しました:', error);
});
Note: Promise.allは、1つのPromiseが拒否されるとすべてが失敗します。部分的な失敗を許容する場合はPromise.allSettledを使用します。
Promise.allSettledの使用
// Promise.allSettledで部分的な失敗を許容
const promise1 = fetch('https://api.example.com/data1').then(r => r.json());
const promise2 = fetch('https://api.example.com/data2').then(r => r.json());
const promise3 = fetch('https://api.example.com/data3').then(r => r.json());
Promise.allSettled([promise1, promise2, promise3])
.then(results => {
results.forEach((result, index) => {
if (result.status === 'fulfilled') {
console.log(`Promise ${index + 1} 成功:`, result.value);
} else {
console.error(`Promise ${index + 1} 失敗:`, result.reason);
}
});
});
カスタムエラークラスの作成
// カスタムエラークラスを定義
class APIError extends Error {
constructor(message, status) {
super(message);
this.name = 'APIError';
this.status = status;
}
}
async function fetchDataWithCustomError() {
try {
const response = await fetch('https://api.example.com/data');
if (!response.ok) {
throw new APIError(
`API呼び出しに失敗しました`,
response.status
);
}
const data = await response.json();
return data;
} catch (error) {
if (error instanceof APIError) {
console.error(`APIエラー (ステータス: ${error.status}):`, error.message);
} else {
console.error('予期しないエラー:', error);
}
throw error;
}
}
タイムアウト処理の実装
// タイムアウト機能付きのPromise処理
function fetchWithTimeout(url, timeout = 5000) {
return Promise.race([
fetch(url),
new Promise((_, reject) =>
setTimeout(() => reject(new Error('リクエストがタイムアウトしました')), timeout)
)
]);
}
fetchWithTimeout('https://api.example.com/data', 5000)
.then(response => response.json())
.then(data => {
console.log('データ取得成功:', data);
})
.catch(error => {
console.error('エラー:', error.message);
});
よくある間違い
間違い1: catchを使わずにPromiseを放置
// ❌ 間違った例
fetch('https://api.example.com/data')
.then(response => response.json())
.then(data => {
console.log('データ:', data);
});
// catchが無いため、エラーが未処理になる可能性がある
// ✅ 正しい例
fetch('https://api.example.com/data')
.then(response => response.json())
.then(data => {
console.log('データ:', data);
})
.catch(error => {
console.error('エラー:', error);
});
間違い2: エラーメッセージの無視
// ❌ 間違った例
async function fetchData() {
try {
const response = await fetch('https://api.example.com/data');
const data = await response.json();
return data;
} catch (error) {
// エラーを完全に無視している
return null;
}
}
// ✅ 正しい例
async function fetchData() {
try {
const response = await fetch('https://api.example.com/data');
const data = await response.json();
return data;
} catch (error) {
console.error('データ取得エラー:', error);
throw error; // エラーを呼び出し元に伝える
}
}
間違い3: Promiseチェーンの破断
// ❌ 間違った例
fetch('https://api.example.com/data')
.then(response => {
response.json();
// returnがないため、次のthenに値が渡されない
})
.then(data => {
console.log('データ:', data); // undefinedになる
});
// ✅ 正しい例
fetch('https://api.example.com/data')
.then(response => {
return response.json(); // returnを忘れない
})
.then(data => {
console.log('データ:', data);
})
.catch(error => {
console.error('エラー:', error);
});
間違い4: try-catchでawaitしていない
// ❌ 間違った例
async function fetchData() {
try {
const promise = fetch('https://api.example.com/data'); // awaitがない
const response = promise.then(r => r.json());
} catch (error) {
console.error('エラー:', error); // catchされない
}
}
// ✅ 正しい例
async function fetchData() {
try {
const response = await fetch('https://api.example.com/data'); // awaitを使用
const data = await response.json();
return data;
} catch (error) {
console.error('エラー:', error);
}
}
まとめ
JavaScriptのPromiseエラーは、適切なエラーハンドリングで多くの場合防ぐことができます。本記事で紹介した重要なポイントをまとめます:
- 必ずcatchメソッドを使用する:すべてのPromiseチェーンに.catch()を追加し、エラーが未処理にならないようにしましょう。
- async/awaitではtry-catchを使用する:async/awaitの場合はtry-catchブロックで囲み、予期しないエラーに対応できるようにします。
- エラーログを出力する:console.errorやロギングライブラリを使用して、エラー情報を記録します。
- ユーザーへのフィードバックを忘れない:エラーが発生した場合、ユーザーに対して適切なメッセージを表示します。
- Promiseチェーンの破断に注意:returnを忘れずに、Promiseチェーンが正しく継続するようにします。
- HTTPレスポンスのステータスチェック:fetchはHTTPエラー時もPromiseを解決するため、response.okの確認が必要です。
これらのベストプラクティスを実装することで、より堅牢で保守性の高いJavaScriptコードを書くことができます。Promiseエラーに直面した場合は、まずエラーハンドリングが適切に実装されているか確認してみてください。

