Async/Await のアンチパターン
「JavaScript、Node.js、非同期」における「new Promise() コンストラクタ内で async/await を使うのはアンチパターンか?」の解説
日本語解説
JavaScriptの非同期処理において、new Promise()
コンストラクタ内で async/await
を使用する手法は、一般的にアンチパターンとされています。その理由を以下に説明します。
アンチパターンである理由
- パフォーマンス低下
async/await
は、非同期処理を同期的なコードのように扱える便利な構文ですが、その内部ではジェネレーター関数やマイクロタスクキューを使用しています。これにより、特に小さな処理の場合には、パフォーマンスが低下する可能性があります。 - 可読性低下
new Promise()
コンストラクタ内でasync/await
を使うと、コードの構造が複雑になり、可読性が低下する可能性があります。特に、複数のasync/await
がネストされている場合、理解が難しくなることがあります。 - エラー処理の難しさ
async/await
を使った非同期処理では、エラー処理が比較的シンプルになります。しかし、new Promise()
コンストラクタ内でasync/await
を使う場合、エラー処理のロジックが複雑になることがあります。
代替手法
new Promise()
コンストラクタ内で async/await
を使う代わりに、以下のような方法が推奨されます。
- Promise.race() を使う
const promise = Promise.race([fetchData(), fetchTimeout()]);
- Promise.all() を使う
const promises = [fetchData(), fetchOtherData()]; const results = Promise.all(promises);
- 直接 Promise.resolve() を使う
const promise = Promise.resolve(fetchData());
「new Promise() コンストラクタ内で async/await を使う」のアンチパターン例
JavaScript
// アンチパターン
function fetchData() {
return new Promise(async (resolve, reject) => {
try {
const response = await fetch('https://api.example.com/data');
const data = await response.json();
resolve(data);
} catch (error) {
reject(error);
}
});
}
解説
このコードは、new Promise()
コンストラクタ内で async/await
を使用しています。これはアンチパターンです。代わりに、以下のように直接 Promise.resolve()
を使う方が効率的で可読性が高いです。
// 良い例
function fetchData() {
return fetch('https://api.example.com/data')
.then(response => response.json())
.catch(error => {
throw error; // エラーを再スロー
});
}
Async/Await の他のアンチパターン
非同期処理の入れ子
async function fetchData() {
const data1 = await fetch('https://api.example.com/data1');
const data2 = await fetch('https://api.example.com/data2');
const result = await processData(data1, data2);
return result;
}
このコードは、複数の async/await
が入れ子になっています。これにより、コードが複雑になり、理解が難しくなります。代わりに、Promise.all()
を使って並列処理を行う方が効率的で可読性が高いです。
async function fetchData() {
const [data1, data2] = await Promise.all([
fetch('https://api.example.com/data1'),
fetch('https://api.example.com/data2'),
]);
const result = await processData(data1, data2);
return result;
}
エラー処理の誤り
async function fetchData() {
try {
const response = await fetch('https://api.example.com/data');
const data = await response.json();
return data;
} catch (error) {
console.error(err or);
return null; // エラーを無視
}
}
このコードは、エラーが発生した場合に null
を返しています。これは、エラーを適切に処理していないため、問題が発生する可能性があります。代わりに、エラーを再スローするか、エラーを適切に処理する必要があります。
async function fetchData() {
try {
const response = await fetch('https://api.example.com/data');
const data = await response.json();
return data;
} catch (err or) {
throw error; // エラーを再スロー
}
}
直接 Promise.resolve() を使う:
function fetchData() {
return Promise.resolve(fetch('https://api.example.com/data')
.then(response => response.json())
.catch(error => {
throw error; // エラーを再スロー
}));
}
Promise.all() を使う:
async function fetchData() {
const [data1, data2] = await Promise.all([
fetch('https://api.example.com/data1'),
fetch('https://api.example.com/data2'),
]);
const result = await processData(data1, data2);
return result;
}
function fetchData() {
return Promise.race([
fetch('https://api.example.com/data'),
new Promise(resolve => setTimeout(() => resolve('timeout'), 5000)) // タイムアウト処理
]);
}
- async/await のチェーンを分割する
async function fetchData() { const data1 = await fetch('https://api.example.com/data1'); const data2Promise = fetch('https://api.example.com/data2'); const data2 = await data2Promise; const result = await processData(data1, data2); return result; }
- Promise.all() を使う
上記の例を参照。
- エラーを適切に処理する
async function fetchData() { try { const response = await fetch('https://api.example.com/data'); const data = await response.json(); return data; } catch (err or) { // エラーをログに記録する console.error('Error:', error); // デフォルト値を返す return { message: 'Error occurred' }; } }
- エラーを再スローする
上記の例を参照。
javascript node.js asynchronous