Hàm
Generator
Generator function và iterator helpers của Node.js so với Iterator (Rust), AsyncStream (Swift), virtual thread (Java) và `iter.Seq`/channel trong Go.
- Phiên bản tối thiểu
- Node.js ≥ 22Go ≥ 1.23Rust ≥ 1.58Swift ≥ 5.7Java ≥ 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 có generator function (function* / yield) built-in: gọi hàm không chạy gì cả, mỗi lần gọi .next() mới chạy tới yield tiếp theo. Không ngôn ngữ nào khác trong danh sách có yield thật. Rust không cần giả lập gì nhiều: Iterator vốn đã "pull" từng giá trị một qua .next(), nên std::iter::from_fn — một closure giữ state và trả Option<T> — gần như là generator viết tay. Swift dùng AsyncStream, thứ duy nhất trong 5 ngôn ngữ có một API tên đúng là yield() — nhưng khác với from_fn của Rust vốn lazy (chỉ chạy khi bị gọi .next()), closure của AsyncStream chạy eager ngay khi được tạo và đệm (buffer) sẵn các giá trị yield ra. Java không có gì dựng sẵn, nhưng từ bản 21 có virtual thread: chạy phần thân generator trên một virtual thread và đồng bộ qua SynchronousQueue để mô phỏng yield chặn (blocking). Go không có cú pháp generator, nhưng từ 1.23 có iter.Seq — kiểu "range-over-func" giúp bạn range trực tiếp qua một hàm giống hệt range qua slice hay map. Trước đó (và vẫn còn hợp lệ), cách kinh điển là dùng channel.
Định nghĩa generator
function* generator() {
yield 'hello';
yield 'world';
}package main
import (
"fmt"
"iter"
)
// Generator trả về một iter.Seq — kiểu "range-over-func" chuẩn của Go.
// Caller range trực tiếp qua nó.
func Generator() iter.Seq[string] {
return func(yield func(string) bool) {
for _, v := range []string{"hello", "world"} {
if !yield(v) {
return
}
}
}
}// Iterator của Rust vốn đã "pull": from_fn nhận một closure giữ state
// riêng và trả Option<T> mỗi lần gọi — gần nhất với generator viết tay.
fn generator() -> impl Iterator<Item = &'static str> {
let mut state = 0;
std::iter::from_fn(move || {
state += 1;
match state {
1 => Some("hello"),
2 => Some("world"),
_ => None,
}
})
}// AsyncStream: closure gọi yield() cho từng giá trị, finish() khi xong —
// gần nhất với `yield` thật trong 5 ngôn ngữ này.
func generator() -> AsyncStream<String> {
AsyncStream { continuation in
continuation.yield("hello")
continuation.yield("world")
continuation.finish()
}
}// Chạy phần thân generator trên một virtual thread (JEP 444, Java 21);
// mỗi lần next() được gọi sẽ chặn cho tới khi thread kia gửi giá trị
// tiếp theo qua SynchronousQueue — mô phỏng `yield` chặn (blocking).
static class Generator implements Iterable<String>, Iterator<String> {
private static final Object DONE = new Object();
private final SynchronousQueue<Object> queue = new SynchronousQueue<>();
private Object peeked;
Generator() {
Thread.ofVirtual().start(() -> {
send("hello");
send("world");
send(DONE);
});
}
private void send(Object value) {
try {
queue.put(value);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}
@Override
public boolean hasNext() {
if (peeked == null) {
try {
peeked = queue.take();
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
return false;
}
}
return peeked != DONE;
}
@Override
public String next() {
if (!hasNext()) throw new NoSuchElementException();
String value = (String) peeked;
peeked = null;
return value;
}
@Override
public Iterator<String> iterator() {
return this;
}
}Lấy giá trị thủ công (next() / done)
const gen = generator();
while (true) {
const { value, done } = gen.next();
console.log(value, done);
if (done) {
break;
}
}
// hello false
// world false
// undefined truefunc main() {
// iter.Pull chuyển một iter.Seq kiểu "push" thành cặp next/stop kiểu
// "pull" — cách gần nhất với việc tự gọi gen.next().
next, stop := iter.Pull(Generator())
defer stop() // luôn gọi stop, kể cả khi sequence đã chạy hết
for {
v, ok := next()
if !ok {
break
}
fmt.Println(v)
}
// hello
// world
}fn main() {
// Iterator vốn đã "pull": gọi .next() trực tiếp, không cần chuyển đổi
// gì thêm (khác với Go, phải dùng iter.Pull để có next/stop).
// (đặt tên `it` vì `gen` là từ khoá dành riêng từ edition 2024)
let mut it = generator();
while let Some(value) = it.next() {
println!("{value}");
}
// None đóng vai trò done: true khi vòng lặp dừng
// hello
// world
}// makeAsyncIterator() lấy iterator thủ công; next() trả nil khi hết —
// đóng vai trò done: true.
do {
var iterator = generator().makeAsyncIterator()
while let value = await iterator.next() {
print(value)
}
}
// hello
// worldvoid main() {
// hasNext()/next() chuẩn của Java: hasNext() chặn cho tới khi có giá
// trị tiếp theo hoặc generator báo xong.
Generator gen = new Generator();
while (gen.hasNext()) {
IO.println(gen.next());
}
// hello
// world
}Duyệt bằng for...of / iterator helpers
for (const value of generator()) {
console.log(value);
}
// hello
// world
// generator object cũng là iterator, nên iterator helpers biến đổi được
// trực tiếp, không cần spread ra mảng trước
for (const value of generator().map((word) => word.toUpperCase())) {
console.log(value);
}
// HELLO
// WORLD// channelGenerator mô phỏng generator bằng một goroutine gửi giá trị qua
// channel, đóng lại khi xong — cách kinh điển để lấy từng giá trị một.
func channelGenerator() chan string {
c := make(chan string)
go func() {
defer close(c)
c <- "hello"
c <- "world"
}()
return c
}
func main() {
for value := range Generator() {
fmt.Println(value)
}
// hello
// world
// cách khác: dùng channel kiểu kinh điển
for value := range channelGenerator() {
fmt.Println(value)
}
// hello
// world
}fn main() {
for value in generator() {
println!("{value}");
}
// hello
// world
// Iterator có sẵn .map()/.filter()/... từ bản 1.0 — lâu trước cả
// iterator helpers của JS (Node 22) hay Go (1.23).
for value in generator().map(|w| w.to_uppercase()) {
println!("{value}");
}
// HELLO
// WORLD
}for await value in generator() {
print(value)
}
// hello
// world
// AsyncStream tuân theo AsyncSequence nên có .map() giống Sequence thường
for await value in generator().map({ $0.uppercased() }) {
print(value)
}
// HELLO
// WORLDvoid main() {
for (String value : new Generator()) {
IO.println(value);
}
// hello
// world
// Iterator/Iterable của Java không có .map() dựng sẵn: chuyển qua
// Stream trước rồi mới biến đổi được.
StreamSupport.stream(new Generator().spliterator(), false)
.map(String::toUpperCase)
.forEach(IO::println);
// HELLO
// WORLD
}Channel vẫn là một cách hợp lệ để mô hình hoá generator giữa các goroutine — nó có từ trước Go 1.23 và không bị iter.Seq thay thế hoàn toàn. Go cũng không có chuỗi iterator helper dựng sẵn (.map(), .filter(), …); bạn phải biến đổi giá trị ngay trong vòng lặp. Rust và Swift có: Iterator/AsyncSequence đều có .map() dựng sẵn từ lâu. Java thì giống Go — Iterator trần không có .map(), phải đổi sang Stream trước.
Tham khảo: github.com/miguelmota/golang-for-nodejs-developers#generators