Generator functions and the yield keyword

JavaScript has many powerful features, and among them are generator functions and the yield keyword. These tools allow you to manage sequence generation and asynchronous programming more effectively. In this blog post, we'll dive deep into what generator functions are, how to use them, and when they might come in handy.

What is a Generator Function?

A generator function is a special type of function that can pause and resume its execution. Unlike regular functions, which run to completion once invoked, generator functions can yield control back to the caller at any point, allowing for interesting use cases like lazy evaluation and asynchronous programming.

Generator functions are defined using the function* syntax. Here’s a simple example:

function* simpleGenerator() { yield 1; yield 2; yield 3; } const gen = simpleGenerator(); console.log(gen.next().value); // 1 console.log(gen.next().value); // 2 console.log(gen.next().value); // 3

**Understanding the **yield Keyword

The yield keyword is used to pause the generator function and send a value back to the caller. When the function is resumed, execution continues from the point where it was paused.

function* countToThree() { yield 1; yield 2; yield 3; } const counter = countToThree(); console.log(counter.next()); // { value: 1, done: false } console.log(counter.next()); // { value: 2, done: false } console.log(counter.next()); // { value: 3, done: false } console.log(counter.next()); // { value: undefined, done: true }

State Retention in Generators

One of the key features of generator functions is their ability to retain state between executions. This makes it very useful for implementing sequences or for generators that manage stateful logic.

function* idGenerator() { let id = 0; while(true) { yield id++; } } const idGen = idGenerator(); console.log(idGen.next().value); // 0 console.log(idGen.next().value); // 1 console.log(idGen.next().value); // 2

**Delegating Generators with **yield*

The yield* keyword allows a generator to delegate execution to another generator. This can be useful for composing generator functions together.

function* subGenerator() { yield 'a'; yield 'b'; yield 'c'; } function* mainGenerator() { yield 1; yield* subGenerator(); yield 2; yield* [3, 4, 5]; } const gen = mainGenerator(); console.log(gen.next().value); // 1 console.log(gen.next().value); // a console.log(gen.next().value); // b console.log(gen.next().value); // c console.log(gen.next().value); // 2 console.log(gen.next().value); // 3 console.log(gen.next().value); // 4 console.log(gen.next().value); // 5

**Passing Arguments to and from **yield

You can pass arguments into and out of generator functions, adding further flexibility to their usage.

function* logGenerator() { console.log("Start"); console.log(yield); console.log(yield); console.log("End"); } const logger = logGenerator(); logger.next(); // logs "Start" logger.next("Message 1"); // logs "Message 1" logger.next("Message 2"); // logs "Message 2" and "End"

**Handling Control Flow with **return and throw

Generators provide return and throw methods to control flow even more granularly.

  • return(value): This method returns the given value and finishes the generator.
function* generatorWithReturn() { yield 1; yield 2; return 3; } const gen = generatorWithReturn(); console.log(gen.next()); // { value: 1, done: false } console.log(gen.next()); // { value: 2, done: false } console.log(gen.next()); // { value: 3, done: true } console.log(gen.next()); // { value: undefined, done: true }
  • throw(error): This method throws an error inside the generator function which can be caught and handled.
function* errorHandlingGenerator() { try { yield 1; yield 2; } catch (e) { console.log("Error caught: ", e); } } const gen = errorHandlingGenerator(); console.log(gen.next().value); // 1 console.log(gen.throw(new Error("Something went wrong!"))); // Error caught: Something went wrong! console.log(gen.next()); // { value: undefined, done: true }

Generator Methods within Objects and Classes

Generators can also be defined within objects and classes, making them versatile in different contexts.

  • As Object Properties:
const obj = { *generator() { yield "first"; yield "second"; } }; const gen = obj.generator(); console.log(gen.next()); // { value: 'first', done: false } console.log(gen.next()); // { value: 'second', done: false }
  • As Class Methods:
class MyClass { *generator() { yield "class 1"; yield "class 2"; } } const obj = new MyClass(); const gen = obj.generator(); console.log(gen.next()); // { value: 'class 1', done: false } console.log(gen.next()); // { value: 'class 2', done: false }

Practical Use Cases

Infinite Sequences

Generators are perfect for creating potentially infinite sequences:

function* infinite() { let i = 0; while(true) { yield i++; } } const inf = infinite(); console.log(inf.next().value); // 0 console.log(inf.next().value); // 1 console.log(inf.next().value); // 2 // This can go on indefinitely...

Custom Iterables

Generators can help in creating custom iterable objects, making them iterable through for..of loops and more.

const customIterable = { *[Symbol.iterator]() { yield 'a'; yield 'b'; yield 'c'; } }; for (const value of customIterable) { console.log(value); // 'a', 'b', 'c' }

Conclusion

Generator functions and the yield keyword are powerful tools in JavaScript that open up a plethora of possibilities. Whether you're managing sequence generation, implementing lazy evaluation, or handling complex asynchronous flows, these constructs offer a level of control that's both robust and readable.

As you start incorporating generators into your code, you'll find they can greatly simplify both the logic and readability of your complex tasks. Happy coding!

Start Building in JavaScript Today

Lilbots is the best platform for creating powerful JavaScript bots

  • Built in access to cutting edge AI models
  • Out of the box integration with hundreds of APIs
  • Collaborate with coding workspaces
  • Discover and fork thousands of bots built by the community

Learn JavaScript

JavaScript Objects

JavaScript Arrays

JavaScript Basics