async/awaitと高階関数
配列内の要素に対して非同期処理を実行する必要がある場合、async/awaitとmapやfilterなどの高階関数を組み合わせることで、効率的な実装が可能になります。
// 非同期フィルター関数
async function asyncFilter(collection, predicate) {
const results = await Promise.all(collection.map(predicate));
return collection.filter((_item, index) => results[index]);
}
async function checkOddNumber(n) {
await sleep(100); // 非同期処理を模倣
return n % 2 !== 0;
}
async function filterOddNumbers(numbers) {
return asyncFilter(numbers, checkOddNumber);
}
filterOddNumbers([1, 2, 3, 4, 5]).then(); // 結果: [1, 3, 5]
同時実行数の制御
ファイルアップロードなどの処理においては、システムリソースの消費抑えるため、同時に実行する非同期処理の数を制限する必要があります。
async function asyncPool(limit, items, task) {
const results = [];
const activePromises = [];
for (const item of items) {
const p = Promise.resolve().then(() => task(item, items));
results.push(p);
if (limit <= items.length) {
const e = p.then(() => activePromises.splice(activePromises.indexOf(e), 1));
activePromises.push(e);
if (activePromises.length >= limit) {
await Promise.race(activePromises);
}
}
}
return Promise.all(results);
}
async function uploadFile(file) {
// ファイルアップロードのロジック
}
async function limitedFileUpload(files) {
return asyncPool(3, files, uploadFile);
}
再帰処理のasync/await最適化
async/awaitを使用することで、再帰関数においても非同期処理を簡潔に実装できます。
// 非同期再帰関数
async function asyncTraverse(nodes) {
for (const node of nodes) {
await processNode(node);
if (node.children) {
await asyncTraverse(node.children);
}
}
}
async function processNode(node) {
// 非同期処理のロジック
}
クラスの非同期インスタンス生成
JavaScriptにおいて、クラスのコンストラクターを非同期にすることはできませんが、ファクトリーパターンを使うことでインスタンスの非同期初期化を実現できます。
class DataHolder {
constructor(content) {
this.content = content;
}
static async build() {
const content = await fetchData(); // 非同期でデータを取得
return new DataHolder(content);
}
}
// 使用例
DataHolder.build().then((instance) => {
// 非同期初期化されたクラスインスタンスを使用
});
async関数におけるawaitによるチェーン呼び出し
awaitを使用することで、チェーン呼び出し内の非同期処理を直感的な順序で実行できます。
class ApiClient {
constructor() {
this.data = null;
}
async fetchFirst() {
this.data = await fetch('/first-url').then(r => r.json());
return this;
}
async fetchSecond() {
this.data = await fetch('/second-url').then(r => r.json());
return this;
}
}
// 使用例
const client = new ApiClient();
const data = await client.fetchFirst().then(c => c.fetchSecond());
async/awaitとイベントループの組み合わせ
async/awaitを活用することで、イベントループをより精细的に制御できます。DOMイベントの処理やタイマー機能などに特に有効です。
// 非同期タイマー関数
async function asyncSetTimeout(callback, milliseconds) {
await new Promise(resolve => setTimeout(resolve, milliseconds));
callback();
}
asyncSetTimeout(() => console.log('2秒後に実行'), 2000);
async/awaitによるエラーハンドリングの簡素化
async/awaitを使用することで、同期処理のコードに近い形でエラーハンドリングを実装できます。
async function asyncOperation() {
try {
const data = await potentiallyFailingOperation();
return data;
} catch (error) {
handleError(error);
}
}
async function potentiallyFailingOperation() {
// 失敗する可能性のある非同期処理
}
function handleError(error) {
// エラーハンドリングのロジック
}