JavaScript Intro To Generator

JavaScript generators are a powerful feature introduced in ECMAScript 2015 (ES6) that allow you to work with asynchronous programming, manage state, and control the execution of functions more flexibly.

Generators are functions that can be paused and resumed, making them ideal for tasks that require iterative processes or asynchronous operations.

This chapter will provides an introduction to generators in JavaScript, explaining their syntax and usage with examples.

What is a Generator?

A generator is a special type of function that can pause its execution and later resume from where it paused.

This is achieved using the function*and yieldkeyword.

Generators return an iterator object that conforms to the iterable protocol.

Example of a Simple Generator


function* simpleGenerator () {
  yield 1;
  yield 2;
  yield 3;
}

const generator = simpleGenerator();

console.log(generator.next()); // { value: 1, done: false }
console.log(generator.next()); // { value: 1, done: false }
console.log(generator.next()); // { value: 1, done: false }
console.log(generator.next()); // { value: undefined, done: true }
            

In the above example, simpleGeneratoris a generator function that yields three values, one at a time.

Each call to next()returns an object with valueand doneproperties, indicating the yielded value and whether the generator is finished.

Example


function* simpleGenerator () {
  yield 1;
  yield 2;
  yield 3;
}

const generator = simpleGenerator();

console.log(generator.next()); // { value: 1, done: false }
console.log(generator.next()); // { value: 1, done: false }
console.log(generator.next()); // { value: 1, done: false }
console.log(generator.next()); // { value: undefined, done: true }

Yielding Values

The yield keyword is used to pause the generator function and return a value to the caller.

When the generator is resumed, it continues execution immediately after the yield statement.

Example with Yield


function* countUpTo (max) {
  for(let i = 0; i <= max; i++){ 
    yield i;
  }
}

const counter = countUpTo(5);

for(const value of counter){ 
  console.log(value); // 1 2 3 4 5
}
            

In the above example, the countUpTogenerator function yields values from 1 to the specified max.

Using a for...ofloop, we can iterate over the values yielded by the generator.

Example


function* countUpTo (max) {
  for(let i = 0; i <= max; i++){ 
    yield i;
  }
}

const counter = countUpTo(5);

for(const value of counter){ 
  console.log(value); // 1 2 3 4 5
}

Controlling Execution

Generators allow you to control the flow of execution, making them suitable for scenarios where you need to pause and resume execution, such as in asynchronous programming.

Example with Pausing and Resuming Execution


function* controlledExecution () {
  console.log("Start");
  yield;
  console.log("Paused");
  yield;
  console.log("Resumed");
}

const controller = controlledExecution();

controller.next(); // Start
controller.next(); // Paused
controller.next(); // Resumed
            

In the above example, the controlledExecutiongenerator function demonstrates how execution can be paused and resumed at specific points using yield.

Example


function* controlledExecution () {
  console.log("Start");
  yield;
  console.log("Paused");
  yield;
  console.log("Resumed");
}

const controller = controlledExecution();

controller.next(); // Start
controller.next(); // Paused
controller.next(); // Resumed

Generators and Iterators

Generators produce iterator objects that can be used in conjunction with JavaScript’s iteration protocols.

This allows generators to be used with constructs like for...of loops and spread syntax.

Example with Iterators


function* fibonacci (n) {
  let a = 0;
  let b = 1;
  while (n-- > 0){ 
    yield a;
    [a, b] = [b, a + b];
  }
}

const fibSequence = fibonacci(5);
console.log([...fibSequence]); // [0, 1, 1, 2, 3]
            

In the above example, the fibonaccigenerator function produces the first n numbers in the Fibonacci sequence.

The generated sequence is then converted into an array using the spread syntax.

Example


function* fibonacci (n) {
  let a = 0;
  let b = 1;
  while (n-- > 0){ 
    yield a;
    [a, b] = [b, a + b];
  }
}

const fibSequence = fibonacci(5);
console.log([...fibSequence]); // [0, 1, 1, 2, 3]

Generators in Asynchronous Programming

Generators can be used to simplify asynchronous programming by yielding promises and using asyncfunctions to handle asynchronous operations more naturally.

Example with Asynchronous Generators


function asyncGenerator () {
  let result1 = yield fetch("https://jsonplaceholder.typicode.com/posts/1");
  console.log(result1);
  let result2 = yield fetch("https://jsonplaceholder.typicode.com/posts/2");
  console.log(result2);
}

function run() {
  const iterator = generator();
  
  function process(result) {
    if (result.done){ 
      return result.value;
    }
      return result.value.then(res => res.json()).then(data => process(iterable.next(data)))
    }
    process(iterable.next(data));
  }

  run(asyncGenerator);
            

In the above example, the asyncGeneratorfunction yields yield promises.

The runfunction processes these promises, resuming the generator function once each promise is resolved.

This pattern allows handling asynchronous operations in a synchronous-like manner.

Generators in JavaScript provide a powerful mechanism for managing function execution, handling iterative processes, and simplifying asynchronous programming.

By using the function*syntax and the yield keyword, you can create flexible and maintainable code.

Example


function asyncGenerator () {
let result1 = yield fetch("https://jsonplaceholder.typicode.com/posts/1");
console.log(result1);
let result2 = yield fetch("https://jsonplaceholder.typicode.com/posts/2");
console.log(result2);
}

function run() {
const iterator = generator();

function process(result) {
  if (result.done){ 
    return result.value;
  }
    return result.value.then(res => res.json()).then(data => process(iterable.next(data)))
  }
  process(iterable.next(data));
}

run(asyncGenerator);

Let's learn about async, generator and iterator using in the next chapter.