Skip to content

JavaScript Tutorial Iterators and Generators

Generator Return Values and Two-way Communication

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

Generators are not only for producing values. They can also return a final value and receive values.

return

A return inside a generator ends it. The returned value appears in the final { done: true } result, but for...of and spread ignore it.

script.js
JS
function* steps() {
  yield "one";
  yield "two";
  return "finished";
}

const g = steps();
console.log(g.next()); // → { value: one, done: false }
console.log(g.next()); // → { value: two, done: false }
console.log(g.next()); // → { value: finished, done: true }

console.log([...steps()]); // → ["one", "two"]   (the return value is not included)

Sending a value in: next(value)

The argument you pass to next(value) becomes the result of the yield expression where the generator is paused.

script.js
JS
function* conversation() {
  const name = yield "What is your name?";
  const mood = yield `Nice to meet you, ${name}. How are you?`;
  return `${name} is feeling ${mood}.`;
}

const talk = conversation();
console.log(talk.next().value);            // → What is your name?
console.log(talk.next("Maya").value);      // → Nice to meet you, Maya. How are you?
console.log(talk.next("great").value);     // → Maya is feeling great.

The first next() call starts the generator (its argument would be ignored). Each later argument answers the previous yield.

Ending early: return() and throw()

script.js
JS
function* numbers() {
  try {
    yield 1;
    yield 2;
    yield 3;
  } finally {
    console.log("cleanup");
  }
}

const g = numbers();
console.log(g.next().value); // → 1
console.log(g.return("stopped")); // → { value: stopped, done: true }   (and "cleanup" is printed)

g.throw(error) throws an error inside the generator at the paused yield, where it can be caught.

script.js
JS
function* guarded() {
  try {
    yield 1;
  } catch (error) {
    console.log("generator caught:", error.message);
    yield "recovered";
  }
}

const g = guarded();
g.next();
console.log(g.throw(new Error("boom")).value); // → recovered

States of a generator

StateMeaning
suspended at startcreated, not started
suspended at yieldpaused, waiting for next()
runningcurrently executing
completedreturned or threw; next() now gives { value: undefined, done: true }

Where this is used

Two-way communication is the idea behind some older async libraries (before async/await) and behind state machines. In everyday code you will mostly use the simple form: generators that yield values.

lightbulb
Keep it simple
If you only need a sequence of values, use a plain generator with yield. Reach for next(value) and throw() only when you have a clear reason.