Skip to content

JavaScript Tutorial Iterators and Generators

Practical Generator Examples

Lesson 184 of 258 2 min read By Deepak Paswan Updated Oct 4, 2026

Generators earn their place in a few real situations.

1. An ID generator

script.js
JS
function* idGenerator(prefix = "id") {
  let n = 1;
  while (true) {
    yield `${prefix}-${n++}`;
  }
}

const ids = idGenerator("user");
console.log(ids.next().value); // → user-1
console.log(ids.next().value); // → user-2
console.log(ids.next().value); // → user-3

The counter’s state is kept inside the generator, with no global variable.

2. Splitting data into pages

script.js
JS
function* chunk(items, size) {
  for (let i = 0; i < items.length; i += size) {
    yield items.slice(i, i + size);
  }
}

const pages = [...chunk([1, 2, 3, 4, 5, 6, 7], 3)];
console.log(pages); // → [[1, 2, 3], [4, 5, 6], [7]]

3. A lazy pipeline

Because generators produce one value at a time, you can chain steps that only do as much work as needed.

script.js
JS
function* naturals() {
  let n = 1;
  while (true) yield n++;
}

function* filter(iterable, predicate) {
  for (const item of iterable) {
    if (predicate(item)) yield item;
  }
}

function* map(iterable, fn) {
  for (const item of iterable) yield fn(item);
}

function* take(iterable, count) {
  if (count <= 0) return;
  for (const item of iterable) {
    yield item;
    if (--count === 0) return;
  }
}

const evenSquares = take(map(filter(naturals(), n => n % 2 === 0), n => n * n), 4);
console.log([...evenSquares]); // → [4, 16, 36, 64]

Only as many numbers are produced as needed to get four results. (Newer JavaScript also has built-in iterator helpers for this, see Recent Features.)

4. Walking a tree

script.js
JS
const menu = { name: "Home", children: [
  { name: "Products", children: [{ name: "Books", children: [] }] },
  { name: "Contact", children: [] }
] };

function* walk(node) {
  yield node.name;
  for (const child of node.children) {
    yield* walk(child);
  }
}

console.log([...walk(menu)]); // → ["Home", "Products", "Books", "Contact"]

5. Async generators: streams of data

An async function* can await between values, and you loop over it with for await...of. This fits paged APIs and streams.

script.js
JS
const wait = ms => new Promise(r => setTimeout(r, ms));

async function* pages() {
  for (let page = 1; page <= 3; page++) {
    await wait(100);                   // pretend to fetch a page
    yield { page, items: [`item ${page}a`, `item ${page}b`] };
  }
}

async function main() {
  for await (const result of pages()) {
    console.log("page", result.page, result.items.length);
  }
}
main();

When to use a generator

  • Lazy or infinite sequences.
  • Walking nested data.
  • Hiding the state of a process (an ID counter, a paginator).
  • Making a class iterable with little code.

When an array is small and you will use all of it, plain arrays are simpler. Do not use generators just because they are interesting.