jsrosetta

Bất đồng bộ

Stream

Readable/Writable/Transform stream của Node.js so với io.Reader/Writer (Go), std::io::Read/Write (Rust), Pipe (Swift) và InputStream (Java).

Phiên bản tối thiểu
Node.js ≥ 15Go ≥ 1.0Rust ≥ 1.87Swift ≥ 5.5Java ≥ 25
Đã chạy thử trên
Node.js 24.12.0Go 1.27.1Rust 1.98.1Swift 6.2.4Java 25.0.4.1

Code Node.js là ES module: lưu file .mjs hoặc đặt "type": "module" trong package.json.

Node.js mô hình hoá dữ liệu chảy qua bằng các lớp Readable/Writable/Transform. Go và Rust không có lớp riêng cho stream — bất cứ thứ gì thực thi io.Reader/io.Writer (Go) hay trait Read/Write (Rust) đều "là" một stream. Swift dùng lớp Pipe/FileHandle của Foundation cộng với AsyncSequence để đọc bất đồng bộ. Java có sẵn InputStream/OutputStream từ những ngày đầu, và FilterInputStream đóng vai trò một Transform stream — bọc quanh một stream khác để biến đổi dữ liệu khi đọc qua nó.

Đọc và ghi dữ liệu dạng stream (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); // kết thúc stream
inStream.pipe(process.stdout); // bất đồng bộ: bắt đầu chảy, nhưng "foobar" chỉ in ra sau các lệnh write bên dưới
 
const outStream = new Writable({
  write(chunk, encoding, callback) {
    console.log("received: " + chunk.toString("utf8"));
    callback();
  },
});
 
outStream.write(Buffer.from("abc")); // callback đồng bộ: in ra ngay
outStream.write(Buffer.from("xyz")); // callback đồng bộ: in ra ngay
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

Biến đổi dữ liệu khi đang chảy qua (Transform stream)

Một Transform stream vừa đọc, vừa biến đổi, vừa phát lại dữ liệu khi nó chảy qua — giống một "map" cho stream trong Node.js. pipeline() từ node:stream/promises chờ toàn bộ chuỗi xử lý hoàn tất và tự forward lỗi, thay vì phải tự lắng nghe sự kiện 'error'/'finish'.

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");
  },
});
 
// Nguồn dữ liệu trong bộ nhớ (không cần stdin) để ví dụ tự chạy độc lập.
const source = Readable.from(["foo", "bar", "baz"]);
 
await pipeline(source, upper, process.stdout);
// → FOO
// → BAR
// → BAZ

Tham khảo: github.com/miguelmota/golang-for-nodejs-developers#streams