Generators

If we invoke a function in TypeScript, we can assume that once the function starts running, it will always run to completion before any other code can run. However, a new kind of function that may be paused in the middle of execution—one or many times—and resumed later, allowing other code to run during these paused periods, has recently been added to the ECMAScript specification. These new kinds of functions are known as generators.

A generator represents a sequence of values. The interface of a generator object is just an iterator. An iterator implements the following interface:

interface Iterator<T> { 
  next(value?: any): IteratorResult<T>; 
  return?(value?: any): IteratorResult<T>; 
  throw?(e?: any): IteratorResult<T>; 
} 

The next()function can be invoked until it runs out of values.

We can define a generator by using the function keyword followed by an asterisk (*). The yield keyword is used to stop the execution of the function and return a value. Let's look at an example:

function *foo() { 
    yield 1; 
    yield 2; 
    yield 3; 
    yield 4; 
    return 5; 
} 
 
let bar = foo(); 
bar.next(); // Object {value: 1, done: false} 
bar.next(); // Object {value: 2, done: false} 
bar.next(); // Object {value: 3, done: false} 
bar.next(); // Object {value: 4, done: false} 
bar.next(); // Object {value: 5, done: true} 
bar.next(); // Object { done: true } 
Note that some additional types are required by generators if you are targeting ES5. You will need to add es2015.generator and es2015.iterable to your tsconfig.json file:
"lib": [ 
      "es2015.promise", 
      "dom",  
      "es5", 
      "es2015.generator", // new 
      "es2015.iterable" // new 
] 
We will learn more about the lib setting in Chapter 9Automating Your Development Workflow.

As we can see, the preceding iterator has five steps. The first time we call next, the function will be executed until it reaches the first yield statement, and then it will return the value 1 and stop the execution of the function until we invoke the generator's next method again. As we can see, we are now able to stop the function's execution at a given point. This allows us to write infinite loops without causing a stack overflow, as demonstrated in the following example:

function* foo() { 
let i = 1; 
    while (true) { 
        yield i++; 
    } 
} 
 
let bar = foo(); 
bar.next(); // Object {value: 1, done: false} 
bar.next(); // Object {value: 2, done: false} 
bar.next(); // Object {value: 3, done: false} 
bar.next(); // Object {value: 4, done: false} 
bar.next(); // Object {value: 5, done: false} 
bar.next(); // Object {value: 6, done: false} 
bar.next(); // Object {value: 7, done: false} 

The generator will open possibilities for synchronicity, as we can call the generator's next method after an asynchronous event has occurred.