はじめに
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();