jsrosetta

Functions

Generators

Generator functions and iterator helpers in Node.js compared to Iterator (Rust), AsyncStream (Swift), virtual threads (Java), and `iter.Seq`/channels in Go.

Minimum versions
Node.js ≥ 22Go ≥ 1.23Rust ≥ 1.58Swift ≥ 5.7Java ≥ 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 has generator functions (function* / yield) built in: calling the function runs nothing at all, and each call to .next() runs up to the next yield. None of the other languages here have a real yield. Rust doesn't need much faking: Iterator is already "pull"-based via .next(), so std::iter::from_fn — a closure that holds state and returns Option<T> — is almost a hand-written generator. Swift uses AsyncStream, the only one of the five languages with an API literally named yield() — though unlike Rust's lazy from_fn (which only runs when .next() is called), the AsyncStream closure runs eagerly as soon as it's created and buffers whatever it yields. Java has nothing built in, but since version 21 it has virtual threads: run the generator body on a virtual thread and synchronize through a SynchronousQueue to simulate a blocking yield. Go has no generator syntax, but since 1.23 it has iter.Seq — a "range-over-func" type that lets you range directly over a function, just like ranging over a slice or map. Before that (and still valid today), the classic approach was a channel.

Defining a generator

function* generator() {
  yield 'hello';
  yield 'world';
}

Pulling values manually (next() / done)

const gen = generator();
 
while (true) {
  const { value, done } = gen.next();
  console.log(value, done);
 
  if (done) {
    break;
  }
}
// hello false
// world false
// undefined true

Iterating with for...of / iterator helpers

for (const value of generator()) {
  console.log(value);
}
// hello
// world
 
// generator objects are iterators, so iterator helpers can transform them
// directly, without spreading into an array first
for (const value of generator().map((word) => word.toUpperCase())) {
  console.log(value);
}
// HELLO
// WORLD

Channels are still a valid way to model a generator across goroutines — they predate Go 1.23 and aren't fully replaced by iter.Seq. Go also has no built-in iterator-helper chain (.map(), .filter(), …); you'd transform values inline in the loop body instead. Rust and Swift do: Iterator/AsyncSequence both have .map() built in and have for a long time. Java is like Go here — a plain Iterator has no .map(), so you convert to a Stream first.

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