DEV Community

Cover image for Javascript Generators: A Beginner's Guide
Abdulmuqsit Adam
Abdulmuqsit Adam

Posted on • Edited on

Javascript Generators: A Beginner's Guide

Recently, I’ve had to pick up some JavaScript concepts to better reaffirm my understanding before moving on to work with them in React, and Generators was one of those concepts I had some little unrest understanding. I decided to piece together the basics to serve as a beginner’s working guide to generators.

Introduction: What are Generators?

According to the MDN resource

Generators are functions that can be exited and later re-entered. Their context (variable bindings) will be saved across re-entrances.

Generator functions are similar to regular functions, except that they can be paused and resumed. Generators are also very closely related to iterators, in the sense that a generator object is an iterator.

Comparison; Generators and Functions

A regular function such as the one below cannot be stopped before it finishes its task i.e. its last line is executed. It follows something called the run-to-completion model.

function regularFunction() {
  console.log('There')
  console.log('is')
  console.log('no')
  console.log('stopping')
  console.log('me')
}
Enter fullscreen mode Exit fullscreen mode

The only way to exit the regularFunction is by using a return statement or throwing an error. If you call the function again, it will begin the execution from the top again.

In contrast, a generator is a function that can stop midway and then continue from where it stopped. See the illustration below;

Functions vs Generators

Syntax

Generator functions are created using a special syntax by adding an * after the function keyword just like this function *, and can be paused using the yield keyword.

Calling a generator function initially does not execute any of its code; instead, it returns a generator object. Code is only executed and values are returned only when the generator’s next() is called, which then executes code until it encounters the yield keyword, upon which it pauses, until next() is called again.

function * createGenerator() {
  yield 'This';
  yield 'is';
  yield 'a';
  yield 'Generator';
}
const generator = createGenerator(); // assigning the Generator function to the generator constant
generator.next(); // { value: 'This', done: false }
generator.next(); // { value: 'is', done: false }
generator.next(); // { value: 'a', done: false }
generator.next(); // { value: 'Generator', done: false }
generator.next(); // { value: undefined, done: true }

Enter fullscreen mode Exit fullscreen mode

Calling generator.next() repeatedly after our last statement above will only return (or more accurately, yield) the same return object: { value: undefined, done: true }.

Generators in Action

Looping through an Array:

Using a for of loop we can iterate over our generator and yield the content at each loop.

// create an array of animals
const animalsList = ['Cat', 'Dog', 'Monkey', 'Bird', 'Fish'];

// create our looping generator
function* loop(arr) {
  for (const item of arr) {
    yield `I like a ${item} as a pet`;
  } 
}

const animalGenerator = loop(animalsList);
console.log(animalGenerator.next());
// Object { value: "I like a Cat as a pet", done: false }
console.log(animalGenerator.next());
// Object { value: "I like a Dog as a pet", done: false }
console.log(animalGenerator.next().value);
// "I like a Monkey as a pet"
Enter fullscreen mode Exit fullscreen mode

Our generator will now loop over the array and print one value at a time every time we call .next(), To get only the value, then use .next().value and it will not print the status of the generator.

.return() in Generators

With .return() we can return a given value and finish the generator.

function* animalsList(){
  yield 'Cat';
  yield 'Dog';
  yield 'Monkey';
}

const animals = animalsList();

console.log(animals.return());
// Object { value: undefined, done: true }
Enter fullscreen mode Exit fullscreen mode

We got value: undefined because we did not pass anything in the return().

Summary

It’s not often you need generators. This article is just to provide a basic explanation of javascript generators. There is much more you can do with a generator, You can check out the official MDN documentation for more advanced cases where the use of generators can come in handy.

Top comments (5)

Collapse
 
charlesr1971 profile image
Charles Robertson • Edited

This is a very useful tutorial, but you really need to proof read your writing:

function * createGenerator() {
  yield 'This';
  yield 'is';
  yield 'a';
  yield 'Generator';
}
const generator = createGenerator(); // assigning the Generator function to the generator constant
generator.next(); // { value: 'Hello', done: false }
generator.next(); // { value: 'World', done: false }
generator.next(); // { value: undefined, done: true }
Enter fullscreen mode Exit fullscreen mode

This is wrong. Your console should read:

generator.next(); // { value: 'This', done: false }
generator.next(); // { value: 'is', done: false }
generator.next(); // { value: 'a', done: false }
generator.next(); // { value: 'Generator', done: false }
generator.next(); // { value: undefined, done: true }
Enter fullscreen mode Exit fullscreen mode

And also this:

function* animals list(){
  yield 'Cat';
  yield 'Dog';
  yield 'Monkey';
}

const animals = animalsList();

console.log(animals.return());
// Object { value: undefined, done: true }
Enter fullscreen mode Exit fullscreen mode

Is wrong. It should be:

function* animalsList(){
  yield 'Cat';
  yield 'Dog';
  yield 'Monkey';
}
Enter fullscreen mode Exit fullscreen mode
Collapse
 
muqsitadam profile image
Abdulmuqsit Adam

Ohh Gutted to have totally missed that...
Thank you very much for your observations ad feedback I really appreciate them.

Collapse
 
charlesr1971 profile image
Charles Robertson

No worries. As I said, a great tutorial. 🙏

Collapse
 
artydev profile image
artydev

Nice thank you.
Give an eye here : DemoGen

Regards

Collapse
 
bigkrp profile image
bigkrp

I think, there is no full image of Generator usage if we miss passing to yield arguments. I mean we can pass to some value generator.next('hello') and get it body of the function via yield keyword.

function * generatorCreator() {
    const a = yield 'get a'
    const b = yield 'get b'

    return `${a} ${b}`
}

const generator = generatorCreator()

console.log(generator.next())
console.log(generator.next('hello'))
console.log(generator.next('world'))
Enter fullscreen mode Exit fullscreen mode

-->

{ value: 'get a', done: false }
{ value: 'get b', done: false }
{ value: 'hello world', done: true }
Enter fullscreen mode Exit fullscreen mode