JavaScript
Node.js
TypeScript

マルチプロセス(マルチスレッド)でのFizzBuzzって?

マルチプロセス(マルチスレッド)のプログラムってとても難しいですね。
特にJavaScript(TypeScript)の場合はことさら難しいと思いました。

そこで、練習のためにマルチプロセスでのFizzBuzzを以下の仕様を満たすように作ってみました。

  • FizzBuzz値を算出する複数のワーカープロセス(ワーカースレッド)を使う
  • ワーカープロセス(ワーカースレッド)からメインプロセス(メインスレッド)に算出した結果を送信する
  • メインプロセス(メインスレッド)では(逐次出力ではなく)結果がすべて揃った後に、結果をまとめて出力する
  • 出力する形式(文字列)の細かいところは問わないこととする

以下は、Node.jsのclusterを使ったマルチプロセス版(TypeScript)です。
RxJSだったらどうなるのか少し気になりますね。
もっと簡単にできるのでしょうか?

fizzbuzz.ts
import * as cluster from "cluster";
import * as events from "events";
events.EventEmitter.defaultMaxListeners = 30;

function* range(begin: number, end: number) {
    for (let i = begin; i <= end; i++) {
        yield i;
    }
}

function fizzbuzz(i: number) {
    return i % 15 === 0 ? "FizzBuzz"
        : i % 5 === 0 ? "Buzz"
            : i % 3 === 0 ? "Fizz"
                : i.toString();
}

async function main() {
    if (cluster.isMaster) {
        const promises = Array.from(range(1, 30)).map((i) => {
            return new Promise<[number, string]>((resolve) => {
                cluster.on("message", (worker, [index, result]: [number, string]) => {
                    if (i === index) {
                        resolve([i, result]);
                    }
                });
            });
        });
        for (const i of range(1, 30)) {
            const worker = cluster.fork();
            worker.on("online", () => {
                worker.send(i);
            });
        }
        const map = new Map(await Promise.all(promises));
        console.log(map);

        Object.values(cluster.workers).forEach((w) => w!.kill());
    } else {
        process.on("message", (i) => {
            process.send!([i, fizzbuzz(i)]);
        });
    }
}

if (require.main === module) {
    main();
}

ポイントはPromiseを作ってからfork/sendして、そのあとawaitで待つことです。
結果は以下のようになります。

> Executing task: npm-run ts-node src/fizzbuzz.ts <

Map {
  1 => '1',
  2 => '2',
  3 => 'Fizz',
  4 => '4',
  5 => 'Buzz',
  6 => 'Fizz',
  7 => '7',
  8 => '8',
  9 => 'Fizz',
  10 => 'Buzz',
  11 => '11',
  12 => 'Fizz',
  13 => '13',
  14 => '14',
  15 => 'FizzBuzz',
  16 => '16',
  17 => '17',
  18 => 'Fizz',
  19 => '19',
  20 => 'Buzz',
  21 => 'Fizz',
  22 => '22',
  23 => '23',
  24 => 'Fizz',
  25 => 'Buzz',
  26 => '26',
  27 => 'Fizz',
  28 => '28',
  29 => '29',
  30 => 'FizzBuzz' }