jsrosetta

Async

Streams

How Node.js's Readable/Writable/Transform streams compare to io.Reader/Writer (Go), std::io::Read/Write (Rust), Pipe (Swift), and InputStream (Java).

Minimum versions
Node.js ≥ 15Go ≥ 1.0Rust ≥ 1.87Swift ≥ 5.5Java ≥ 25
Verified on
Node.js 24.12.0Go 1.27.1Rust 1.98.1Swift 6.2.4Java 25.0.4.1

Node.js code is an ES module: save it as .mjs or set "type": "module" in package.json.

Node.js models flowing data with the Readable/Writable/Transform classes. Go and Rust have no dedicated stream class — anything that implements io.Reader/io.Writer (Go) or the Read/Write traits (Rust) "is" a stream. Swift uses Foundation's Pipe/FileHandle classes together with AsyncSequence for asynchronous reads. Java has had InputStream/OutputStream since its earliest days, and FilterInputStream plays the role of a Transform stream — it wraps another stream and transforms the data as it's read through it.

Reading and writing streaming data (Readable/Writable vs io.Reader/io.Writer)

import { Readable, Writable } from "node:stream";
 
const inStream = new Readable();
 
inStream.push(Buffer.from("foo"));
inStream.push(Buffer.from("bar"));
inStream.push(null); // end the stream
inStream.pipe(process.stdout); // async: starts flowing, but "foobar" only prints after the writes below
 
const outStream = new Writable({
  write(chunk, encoding, callback) {
    console.log("received: " + chunk.toString("utf8"));
    callback();
  },
});
 
outStream.write(Buffer.from("abc")); // synchronous callback: prints immediately
outStream.write(Buffer.from("xyz")); // synchronous callback: prints immediately
outStream.end();
$ node streams.js
received: abc
received: xyz
foobar
 
$ go run streams.go
foobar
received: abc
received: xyz
 
$ cargo run -q
foobar
received: abc
received: xyz
 
$ swift main.swift
foobar
received: abc
received: xyz
 
$ java Main.java
foobar
received: abc
received: xyz

Transforming data as it flows through (Transform stream)

A Transform stream reads, modifies, and re-emits data as it flows through — Node's built-in "map" for streams. pipeline() from node:stream/promises awaits the whole chain and forwards any error automatically, instead of juggling 'error'/'finish' listeners by hand.

import { Readable, Transform } from "node:stream";
import { pipeline } from "node:stream/promises";
 
const upper = new Transform({
  transform(chunk, encoding, callback) {
    callback(null, chunk.toString("utf8").toUpperCase() + "\n");
  },
});
 
// In-memory source (no stdin) so this example is self-contained.
const source = Readable.from(["foo", "bar", "baz"]);
 
await pipeline(source, upper, process.stdout);
// → FOO
// → BAR
// → BAZ

Reference: github.com/miguelmota/golang-for-nodejs-developers#streams