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

記事「[【備忘録】Javascriptで非同期処理を効率良くさばく方法](https://qiita.com/tonio0720/items/6f9319e4cce53256b4c9)」の[「同時実行(concurrently)」](https://qiita.com/tonio0720/items/6f9319e4cce53256b4c9#同時実行concurrently)で述べられていた
並列数を固定しての並列処理を
汎用関数に切り出してみた。

```JavaScript
/**
 * 与えられたイテラブルから得られる関数を順に、
 * 指定された数まで並列に実行する。
 * 
 * @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番目の関数の処理を終了した。
```

使用例：

```JavaScript
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();
```

<details><summary>出力例：</summary>
<div>

```
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
```
</div></details>