Generators

Also called function *, generators allow you to create functions whose execution can be paused and then later resumed maintaining the state between pause-resume transitions. The value returned from a generator is called an iterator and can be used to control this pause-resume transition.

Here is a simple example of a generator function that generates an infinite list of whole numbers.

  1. function* wholeNumbers() {
  2. var current = 0;
  3. while(true) {
  4. yield current++;
  5. }
  6. }

The yield contextual keyword is used to return control from a generator (effectively pausing function execution) along with an optional value (here current). You can get access to this value using the iterator‘s .next() member function, this is shown below:

  1. function* wholeNumbers() {
  2. var current = 0;
  3. while(true) {
  4. yield current++;
  5. }
  6. }
  7. var iterator = wholeNumbers();
  8. console.log(iterator.next()); // 0
  9. console.log(iterator.next()); // 1
  10. console.log(iterator.next()); // 2
  11. // so on till infinity....

Now that you have seen function*, yield and .next() we can dig deeper.

Catching Errors

Any errors thrown (intentially using throw or unintentionally due to error) from the generator can be caught using try/catch just like normal function executions. This is demonstrated below:

  1. function* wholeNumbers() {
  2. var current = 0;
  3. while(true) {
  4. if (current === 3)
  5. throw new Error('3 is the magic number');
  6. else
  7. yield current++;
  8. }
  9. }
  10. var iterator = wholeNumbers();
  11. console.log(iterator.next()); // 0
  12. console.log(iterator.next()); // 1
  13. console.log(iterator.next()); // 2
  14. try {
  15. console.log(iterator.next()); // Will throw an error
  16. }
  17. catch(ex) {
  18. console.log(ex.message); // 3 is the magic number
  19. }

Controlling function execution externally

The iterator returned from the generator function can be used to control the state inside the generator function as well.

// TODO: example