Stateful Goroutines in TypeScript

import * as crypto from 'crypto';

interface ReadOp {
    key: number;
    resp: (value: number) => void;
}

interface WriteOp {
    key: number;
    val: number;
    resp: (success: boolean) => void;
}

function main() {
    let readOps = 0;
    let writeOps = 0;

    const reads: ReadOp[] = [];
    const writes: WriteOp[] = [];

    // This function simulates the state-owning goroutine
    function stateManager() {
        const state: { [key: number]: number } = {};

        setInterval(() => {
            if (reads.length > 0) {
                const read = reads.shift()!;
                read.resp(state[read.key] || 0);
            } else if (writes.length > 0) {
                const write = writes.shift()!;
                state[write.key] = write.val;
                write.resp(true);
            }
        }, 0);
    }

    stateManager();

    // Simulate 100 read operations
    for (let r = 0; r < 100; r++) {
        setInterval(() => {
            const read: ReadOp = {
                key: Math.floor(Math.random() * 5),
                resp: () => {
                    readOps++;
                }
            };
            reads.push(read);
        }, 1);
    }

    // Simulate 10 write operations
    for (let w = 0; w < 10; w++) {
        setInterval(() => {
            const write: WriteOp = {
                key: Math.floor(Math.random() * 5),
                val: Math.floor(Math.random() * 100),
                resp: () => {
                    writeOps++;
                }
            };
            writes.push(write);
        }, 1);
    }

    // Let the operations run for a second
    setTimeout(() => {
        console.log("readOps:", readOps);
        console.log("writeOps:", writeOps);
        process.exit(0);
    }, 1000);
}

main();

In this example, we’re using TypeScript to demonstrate a stateful approach using asynchronous operations. This aligns with TypeScript’s event-driven, non-blocking I/O model, which is similar to the concept of goroutines in concurrent programming.

The ReadOp and WriteOp interfaces encapsulate the request structures. The stateManager function simulates the state-owning process, continuously checking for read and write operations and responding to them.

We use setInterval to simulate concurrent operations, both for the state manager and for initiating read and write requests. This allows us to mimic the concurrent nature of the original example.

The crypto.randomBytes function is used to generate random numbers, serving a similar purpose to the math/rand package in the original code.

At the end of the program, we use setTimeout to allow the operations to run for a second before reporting the final counts.

To run this program:

$ ts-node stateful-operations.ts
readOps: 71708
writeOps: 7177

This TypeScript implementation demonstrates a similar concept of managing shared state through message passing, which aligns with TypeScript’s asynchronous programming model. While it doesn’t use the exact same concurrency primitives as the original example, it achieves a similar result in terms of managing state and coordinating multiple operations.