JavaScript
es6
ECMAScript2015

【JavaScript】指定した並列数で並列処理を行う関数

記事「【備忘録】Javascriptで非同期処理を効率良くさばく方法」の「同時実行(concurrently)」で述べられていた

並列数を固定しての並列処理を

汎用関数に切り出してみた。

/**

* 与えられたイテラブルから得られる関数を順に、
* 指定された数まで並列に実行する。
*
* @param iterable {Iterable<() => Promise<void>}
* 実行したい関数を要素に持つイテラブル。
* 各関数は引数を持たず、Promise を返す。
* @param concurrency {number} この数まで並列に実行する。
* @return {Promise<void>}
* 全ての関数を実行し終えると resolve される Promise。
*/

async function runConcurrentlyAsync(iterable, concurrency) {
const iterator = iterable[Symbol.iterator]();
let index = 0; // ログ用
const promises = Array.from({ length: concurrency }, (_, id) => {
return new Promise(async (resolve) => {
for (
let result = iterator.next();
!result.done;
result = iterator.next()
) {
const i = index++;
console.log(`${id}: ${i}...`);

await result.value();

console.log(` ...${id}: ${i}`);
}

resolve();
});
});
await Promise.all(promises);
}

引数はイテラブルなので、配列やジェネレーターなどが使える。

ログには次のような情報が出力される。

0: 10...             // 0本目の並列処理が10番目の関数の処理を開始した。

...0: 10 // 0本目の並列処理が10番目の関数の処理を終了した。

使用例:

async function verySlowAsync(index) {

return new Promise((resolve) => setTimeout(resolve, Math.random() * 1000));
}

function main() {
const INIT = 0;
const MAX = 100;
const CONCURRENCY = 10; // 同時実行できる数を定義

const generator = (function* createGenerator() {
for (let index = INIT; index < MAX; index++) {
yield async () => await verySlowAsync(index);
}
})();

runConcurrentlyAsync(generator, CONCURRENCY);
}
main();

出力例:

0: 0...

1: 1...
2: 2...
3: 3...
4: 4...
5: 5...
6: 6...
7: 7...
8: 8...
9: 9...
...1: 1
1: 10...
...0: 0
0: 11...
...5: 5
5: 12...
...7: 7
7: 13...
...3: 3
3: 14...
...0: 11
0: 15...
...1: 10
1: 16...
...4: 4
4: 17...
...5: 12
5: 18...
...2: 2
2: 19...
...8: 8
8: 20...
...7: 13
7: 21...
...7: 21
7: 22...
...9: 9
9: 23...
...0: 15
0: 24...
...0: 24
0: 25...
...6: 6
6: 26...
...5: 18
5: 27...
...4: 17
4: 28...
...9: 23
9: 29...
...7: 22
7: 30...
...3: 14
3: 31...
...5: 27
5: 32...
...9: 29
9: 33...
...6: 26
6: 34...
...3: 31
3: 35...
...1: 16
1: 36...
...2: 19
2: 37...
...1: 36
1: 38...
...1: 38
1: 39...
...6: 34
6: 40...
...8: 20
8: 41...
...9: 33
9: 42...
...1: 39
1: 43...
...0: 25
0: 44...
...4: 28
4: 45...
...1: 43
1: 46...
...6: 40
6: 47...
...6: 47
6: 48...
...2: 37
2: 49...
...7: 30
7: 50...
...6: 48
6: 51...
...3: 35
3: 52...
...5: 32
5: 53...
...9: 42
9: 54...
...8: 41
8: 55...
...6: 51
6: 56...
...6: 56
6: 57...
...3: 52
3: 58...
...2: 49
2: 59...
...6: 57
6: 60...
...4: 45
4: 61...
...9: 54
9: 62...
...0: 44
0: 63...
...6: 60
6: 64...
...5: 53
5: 65...
...1: 46
1: 66...
...4: 61
4: 67...
...5: 65
5: 68...
...7: 50
7: 69...
...1: 66
1: 70...
...3: 58
3: 71...
...8: 55
8: 72...
...2: 59
2: 73...
...4: 67
4: 74...
...0: 63
0: 75...
...6: 64
6: 76...
...3: 71
3: 77...
...9: 62
9: 78...
...6: 76
6: 79...
...6: 79
6: 80...
...5: 68
5: 81...
...3: 77
3: 82...
...1: 70
1: 83...
...7: 69
7: 84...
...2: 73
2: 85...
...3: 82
3: 86...
...5: 81
5: 87...
...8: 72
8: 88...
...9: 78
9: 89...
...8: 88
8: 90...
...3: 86
3: 91...
...4: 74
4: 92...
...5: 87
5: 93...
...0: 75
0: 94...
...2: 85
2: 95...
...5: 93
5: 96...
...0: 94
0: 97...
...4: 92
4: 98...
...0: 97
0: 99...
...1: 83
...3: 91
...7: 84
...5: 96
...9: 89
...6: 80
...0: 99
...2: 95
...8: 90
...4: 98