Learning Objectives

  • Master the next() method
  • Learn to use return() for early termination
  • Understand throw() for error injection
  • Implement cleanup with finally blocks

The next() Method

The next() method advances the generator and optionally passes a value.

function* example() {
  const a = yield 1;
  const b = yield 2;
  return a + b;
}

const gen = example();
console.log(gen.next());      // { value: 1, done: false }
console.log(gen.next(10));    // { value: 2, done: false }
console.log(gen.next(20));    // { value: 30, done: true }

The return() Method

The return() method terminates the generator early and returns a value.

function* numbers() {
  yield 1;
  yield 2;
  yield 3;
  yield 4;
  yield 5;
}

const gen = numbers();
console.log(gen.next());        // { value: 1, done: false }
console.log(gen.next());        // { value: 2, done: false }
console.log(gen.return('stopped')); // { value: 'stopped', done: true }
console.log(gen.next());        // { value: undefined, done: true }

return() with Cleanup

function* withCleanup() {
  try {
    yield 1;
    yield 2;
    yield 3;
  } finally {
    console.log('Cleanup!');
  }
}

const gen = withCleanup();
console.log(gen.next());        // { value: 1, done: false }
console.log(gen.return('done')); // Logs: Cleanup!
                                // { value: 'done', done: true }

The throw() Method

The throw() method injects an error into the generator.

function* errorHandler() {
  try {
    yield 1;
    yield 2;
    yield 3;
  } catch (error) {
    console.log('Caught:', error.message);
    yield 'recovered';
  }
}

const gen = errorHandler();
console.log(gen.next());                    // { value: 1, done: false }
console.log(gen.throw(new Error('Oops!'))); // Logs: Caught: Oops!
                                            // { value: 'recovered', done: false }
console.log(gen.next());                    // { value: undefined, done: true }

Uncaught Errors

function* noErrorHandler() {
  yield 1;
  yield 2;
}

const gen = noErrorHandler();
gen.next();                    // { value: 1, done: false }

try {
  gen.throw(new Error('Boom!')); // Throws error (not caught in generator)
} catch (error) {
  console.log('Caught outside:', error.message);
}

Real-World: Resource Management

function* fileProcessor(filename) {
  console.log(`Opening ${filename}`);
  
  try {
    yield 'reading';
    yield 'processing';
    yield 'writing';
  } finally {
    console.log(`Closing ${filename}`);
  }
}

const processor = fileProcessor('data.txt');
console.log(processor.next().value);   // Opening data.txt, returns: reading
console.log(processor.next().value);   // returns: processing
processor.return('cancelled');         // Closing data.txt

Error Recovery Pattern

function* resilientTask() {
  let retries = 3;
  
  while (retries > 0) {
    try {
      yield 'attempting';
      yield 'success';
      return 'completed';
    } catch (error) {
      retries--;
      console.log(`Error: ${error.message}, retries left: ${retries}`);
      
      if (retries === 0) {
        throw error;
      }
      
      yield 'retrying';
    }
  }
}

const task = resilientTask();
task.next();                           // attempting
task.throw(new Error('Failed'));       // Error: Failed, retries left: 2
task.next();                           // attempting
task.throw(new Error('Failed again')); // Error: Failed again, retries left: 1
task.next();                           // attempting
task.next();                           // success

Combining All Methods

function* lifecycle() {
  console.log('Start');
  
  try {
    const a = yield 'step1';
    console.log('Got:', a);
    
    const b = yield 'step2';
    console.log('Got:', b);
    
    yield 'step3';
  } catch (error) {
    console.log('Error:', error.message);
    yield 'error-recovery';
  } finally {
    console.log('Cleanup');
  }
}

// Example 1: Normal flow
const gen1 = lifecycle();
gen1.next();           // Start
gen1.next('value1');   // Got: value1
gen1.next('value2');   // Got: value2
gen1.next();           // Cleanup

// Example 2: With error
const gen2 = lifecycle();
gen2.next();                          // Start
gen2.throw(new Error('Something wrong')); // Error: Something wrong, Cleanup

// Example 3: Early return
const gen3 = lifecycle();
gen3.next();           // Start
gen3.return('early');  // Cleanup

Key Takeaways

  • next(value) - Advances generator, passes value
  • return(value) - Terminates early, runs finally
  • throw(error) - Injects error into generator
  • ✅ Use try/catch for error handling
  • ✅ Use finally for cleanup
  • ✅ Perfect for resource management

Next Steps

Now let's explore yield delegation with yield*!