🙆♀️
TypeScript(JavaScript)で複数の非同期関数を直列に実行したい
複数の非同期関数を並列に実行する方法は簡単です。Promise.allを使うだけです。
/*
* 1秒待機してからテキストをコンソール出力する非同期関数
*/
function asyncFunc(text: string) {
return new Promise<void>((resolve) => {
setTimeout(() => {
console.log(text);
resolve();
}, 1000);
});
}
(async function main() {
console.log("start");
const promises = ["1", "2", "3", "4", "5"].map((str) => asyncFunc(str));
await Promise.all(promises); // 並列実行して全ての完了を待ち合わせる
console.log("end");
})();
/* 実行結果 (1~5はほぼ同時に出力される)
start
1
2
3
4
5
end
*/
やりたいこと
では、全ての非同期関数を直列に実行するにはどうすればいいでしょうか。
結論から書いてしまうと以下です。
/*
* 1秒待機してからテキストをコンソール出力する非同期関数
*/
function asyncFunc(text: string) {
return new Promise<void>((resolve) => {
setTimeout(() => {
console.log(text);
resolve();
}, 1000);
});
}
(async function main() {
console.log("start");
await ["1", "2", "3", "4", "5"].reduce(async (acc, cur) => {
await acc;
return asyncFunc(cur);
}, Promise.resolve());
console.log("end");
})();
/* 実行結果 (1~5は1秒毎に出力される)
start
1
2
3
4
5
end
*/
複数の非同期関数を直列に実行するAPIは用意されていないので、自分でreduceなどを使って書く必要があります。
大したコード量ではないですが、初心者としてはArray.reduceはとっつきにくいですし、並列実行のコードよりは見通しが悪いかもしれません。
そう思った場合、同期実行部分を以下のようにutility関数にしてしまうと多少見通しが良くなります。
/*
* 1秒待機してからテキストをコンソール出力する非同期関数
*/
function asyncFunc(text: string) {
return new Promise<void>((resolve) => {
setTimeout(() => {
console.log(text);
resolve();
}, 1000);
});
}
/*
* 非同期関数の配列を渡すと直列実行するutility関数
*/
async function syncronize(asyncFuncs: Array<() => Promise<any>>){
return asyncFuncs.reduce<Promise<any>>(async (acc, cur) => {
await acc;
return cur()
}, Promise.resolve())
}
(async function main() {
console.log("start");
const asuncFuncs = await ["1", "2", "3", "4", "5"].map((str) => () => asyncFunc(str));
await syncronize(asuncFuncs); // 非同期関数の配列を直列に実行
console.log("end");
})();
/* 実行結果 (1~5は1秒毎に出力される)
start
1
2
3
4
5
end
*/
utilityに切り出したコード量は少ないですが、ややこしい部分を多少外に出すことができました。
tips程度の内容ですがググったら意外とHitしなかったので、誰かの助けになればという思いで記事にしておきます。
Discussion
関数ではないですが、
for await of
はどうですか?