Table of contents

  1. 1. Terminology
  2. 2. Generator functions
  3. 3. Iterator objects
  4. 4. Iterable objects
  5. 5. Video Tutorial
  6. 6. Summary

A generator function returns a lazy iterator, which generates data only on demand. In the following tutorial, Benny will show you how to code your own generator functions in TypeScript. You will learn the differences between iterators and iterables and how to refactor your code to use generator functions.

Terminology

Generator functions

A generator function is a function, which returns a generator object. Generator functions get defined with an asterisk next to the function keyword. The return value of a generator function is of type IterableIterator, because every generator object is iterable and follows the iterator protocol. Values get returned with the yield keyword, which returns a value on demand when calling the next function from a generator object. Each subsequent call to next continues processing from where the yield keyword paused.

generator-function.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
function* generateDate(timestamp: number, intervalInMillis: number): IterableIterator<Date> {
let increment = 0;
while (increment < 10) {
increment += 1;
const timeIncrement = increment * intervalInMillis;
yield new Date(timestamp + timeIncrement);
}
}

const start = new Date('2020-11-29T00:00:00.000Z').getTime();
const oneMinuteInMillis = 60_000;

const generatorObject = generateDate(start, oneMinuteInMillis);

// I am an "iterator", you can call `next` on me
const {value} = generatorObject.next();
console.log(value); // "2020-11-29T00:01:00.000Z"

// I am "iterable", you can run me in loops
for (const date of generatorObject) {
console.log(date);
}

Iterator objects

Iterators are objects implementing the iterator protocol. In simplified terms, an iterator is an object with a next method. This next method has to return new objects, each of which has the properties value (of type any) and done (of type boolean).

iterator.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
function generateDate(timestamp: number, intervalInMillis: number) {
let increment = 0;

const dateIterator = {
next: function() {
while (increment < 10) {
increment += 1;
const timeIncrement = increment * intervalInMillis;
const nextDate = new Date(timestamp + timeIncrement);
return {value: nextDate, done: false};
}
return {value: undefined, done: true};
},
};

return dateIterator;
}

const start = new Date('2020-11-29T00:00:00.000Z').getTime();
const oneMinuteInMillis = 60_000;

const iterator = generateDate(start, oneMinuteInMillis);

let result = iterator.next();
while (!result.done) {
result = iterator.next();
console.log(result.value);
}

Iterable objects

An object is iterable, when it defines a default iterator for the key of Symbol.iterator.

iterable.ts
1
2
3
4
5
6
7
8
9
10
11
12
function* generateDate(timestamp: number, intervalInMillis: number): IterableIterator<Date> {
let increment = 0;
while (increment < 10) {
increment += 1;
const timeIncrement = increment * intervalInMillis;
yield new Date(timestamp + timeIncrement);
}
}

const dateIterable = {
[Symbol.iterator]: generateDate,
};

Video Tutorial

Summary