async/awaitの高度な活用方法

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) {
    // エラーハンドリングのロジック
}


タグ: javascript async-await Promise 高階関数 非同期処理

5月14日 00:29 投稿