5
7

forEachでawaitを使おうとしたら怒られた

Posted at

はじめに

forEachでawaitを使おうとしたら、エラーになり、どうやったらループでawaitを使えるか調べたので、簡単に記載します。

await とは?

awaitは非同期処理が終わるまで処理を止めておいてくれる便利なもの
jsはawaitやpromiseを使わないと、処理が終わる前に他の処理を実行してしまうので、直列に処理を実行したいときなどは利用すると良い

// 非同期関数
async function asyncTask(name) {
    console.log(`Task ${name} started.`);
    await new Promise(resolve => setTimeout(resolve, 2000)); // 2秒間スリープ
    console.log(`Task ${name} completed.`);
}

// メイン処理
async function main() {
    console.log("Main started.");

    // `await`を使って非同期処理を待つ
    await asyncTask("A");
    await asyncTask("B");

    // `asyncTask`を`await`なしで呼び出す
    asyncTask("C");
    asyncTask("D");

    console.log("Main completed.");
}

main();
Main started.
Task A started.
Task A completed.
Task B started.
Task B completed.
Task C started.
Task D started.
Main completed.
Task C completed. // このログが出る前にMain処理の最後のログ出力が実行されている
Task D completed.

ループでawaitを使う

forEachでawaitを実行しようとしたが、うまく動作しなかった。
forEachは配列の各要素に対してコールバック関数を実行するものだが、コールバック関数でPromiseを返しても、その解決を待たずに次のコールバックが呼ばれるらしい

じゃあどうする?

解決策1 for of

async function asyncTask(name) {
    console.log(`Task ${name} started.`);
    await new Promise(resolve => setTimeout(resolve, 1000)); // 1秒待機
    console.log(`Task ${name} completed.`);
}

async function runForOf() {
    const tasks = ['A', 'B', 'C'];

    for (const task of tasks) {
        await asyncTask(task);
    }

    console.log('Finished processing with for...of');
}

runForOf();

解決策2 for

async function asyncTask(name) {
    console.log(`Task ${name} started.`);
    await new Promise(resolve => setTimeout(resolve, 1000)); // 1秒待機
    console.log(`Task ${name} completed.`);
}

async function runFor() {
    const tasks = ['A', 'B', 'C'];

    for (let i = 0; i < tasks.length; i++) {
        await asyncTask(tasks[i]);
    }

    console.log('Finished processing with for');
}

runFor();

解決策3 Array.prototype.reduce()

async function asyncTask(name) {
    console.log(`Task ${name} started.`);
    await new Promise(resolve => setTimeout(resolve, 1000)); // 1秒待機
    console.log(`Task ${name} completed.`);
}

async function runReduce() {
    const tasks = ['A', 'B', 'C'];

    await tasks.reduce(async (previousTask, currentTask) => {
        await previousTask;
        return asyncTask(currentTask);
    }, Promise.resolve());

    console.log('Finished processing with reduce');
}

runReduce();

解決策4 Array.prototype.map() & Promise.all()

async function asyncTask(name) {
    console.log(`Task ${name} started.`);
    await new Promise(resolve => setTimeout(resolve, 1000)); // 1秒待機
    console.log(`Task ${name} completed.`);
}

async function runMap() {
    const tasks = ['A', 'B', 'C'];

    await Promise.all(tasks.map(task => asyncTask(task)));

    console.log('Finished processing with map and Promise.all');
}

runMap();

解決策5 for ループと非同期タスクの配列を使用

async function asyncTask(name) {
    console.log(`Task ${name} started.`);
    await new Promise(resolve => setTimeout(resolve, 1000)); // 1秒待機
    console.log(`Task ${name} completed.`);
}

async function runTasks() {
    const tasks = ['A', 'B', 'C'];
    const promises = [];

    for (const task of tasks) {
        promises.push(asyncTask(task));
    }

    await Promise.all(promises);

    console.log('Finished processing with for loop and Promise.all');
}

runTasks();

5
7
2

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
5
7