# Generators and Iterables
# Description
# Iterables
Iterable objects are a generalization of arrays. That’s a concept that allows us to make any object usable in a for..of
loop.
- Iterables must implement a method named
Symbol.iterator
- The object result of the call
[Symbol.iterator]()
is called an iterator - An iterator must have the method named
next()
that returns an object{done: Boolean, value: any}
- The
Symbol.iterator
method is called automatically byfor..of
See the example hello-symbol-iterator.js (opens new window) in the repo ULL-MII-SYTWS-2021/learning-generators (opens new window). (What will be the output?)
Read the chapter Iterables (opens new window) of JavaScript.info reproducing the examples and exercises.
Interesting
Read the section Name Collisions (opens new window) of the article A Practical Guide to Symbols in JavaScript for an explanation of why is Symbol.iterator
a symbol rather than a string.
# Generators
Generators are created by generator functions function* f(…) {…}
.
- Inside generators (only) there exists a
yield
operator. - The outer code and the generator may exchange results via
next/yield
calls.
See the examples
- 01-generator-functions/hello-generators.js (opens new window). (What will be the output?)
- 02-generators-are-iterable (opens new window)
Read the chapter Generators (opens new window) of JavaScript.info reproducing the examples and exercises.
# Exercise Groups in the book EloquentJS Chapter 6
Write an iterable class called
Group
that works like the Set JS class (opens new window). Here is a template for the classGroup
(Exercise Groups (opens new window) in the book EloquentJS Chapter 6):class Group { constructor() { // ... your code } add(elt) { // ... your code } delete(elt) { // ... your code } has(elt) { // ... your code } static from(iterable) { // Takes an iterable object as argument and // creates a group that contains all the values // produced by iterating over it. } *[Symbol.iterator] () { // ... Your code } } export { Group };
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24Make the
Group
class from the previous exercise iterable. (Exercise Iterable groups Groups in EloquentJS Chapter 6 (opens new window))Write the solution as an ES6 module so that can be imported with this syntax:
#!/usr/bin/env node import { Group } from './eloquent-js-6-2-group-with-generators.js'; let group = Group.from([10, 20]); console.log(group.has(10)); // → true console.log(group.has(30)); // → false group.add(10); group.delete(10); console.log(group.has(10)); // → false for (let value of Group.from(['a', 'b', 'c'])) { console.log(value); } // → a // → b // → c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19See the Node.js doc Modules: Packages (opens new window) for more details on the use of ECMA 6 Modules in Node.js.
Simplify the solution to making the
Group
class iterable using a generator instead of a plain iterator as suggested in Chapter 11 of the book Eloquent JS (opens new window)Writing iterators is often much easier when you use generator functions. The iterator for the Group class can be written with this generator:
Group.prototype[Symbol.iterator] = function*() { for (let i = 0; i < this.members.length; i++) { yield this.members[i]; } };
1
2
3
4
5
You can see a solution at folder learning-generators/03-using-generators-for-iterables (opens new window) of the repo ULL-MII-SYTWS-2021/learning-generators
# a = yield exp submits exp and receives the b of the next g.next(b)
You have to take into account these facts:
- When using a generator
g
, you can pass one argument (and only one) tonext
:g.next(a)
- The computation was paused just after the evaluation of the last
yield
expression executed insideg
- The call to
g.next(a)
becomes the result of this lastyield
expression - The first call
generator.next()
should be always made without an argument (If passed the argument will be ignored)
I like to see it this way:
- when a call to
b = g.next(y)
is made, the generator is executed until the nexta = yield exp
expression is reached. - the
yield
stops after the expressionexp
has been evaluated andb
gets the yielded valueexp
- but the execution has paused before the assignment to
a
has been made! - The next call to
g.next(z)
will be set the value returned by theyield
asz
and thusb
will bez
# Exercise one
What is the output of the following code?
function* generator(z) {
console.log(z);
z++;
let a = yield z+1;
console.log('Inside generator: '+a); // a is hello
let b = yield (a+" world!");
console.log('Inside generator: '+b); // b is 10
yield b*2;
}
let g = generator(999);
console.log(g.next().value);
console.log(g.next("hello", "second ignored parameter").value);
console.log(g.next(10).value);
2
3
4
5
6
7
8
9
10
11
12
13
14
Play with the example for different inputs
# Exercise two
What is the output of the following code?
function * gen () {
const returnedFromYield = yield 'foo'
yield returnedFromYield
}
const g = gen()
console.log(g.next(1))
console.log(g.next(2))
console.log(g.next(3))
2
3
4
5
6
7
8
9
10
# Return in a Generator
A `return` statement in a generator, when executed, will make the generator finish:
- If a value
return v;
is returned, it will produce{done: false, value: v}
- The next call to
g.next()
will produce{done: true, value: undefined}
What is the output?
function * foo () {
yield 123
}
function * bar () {
return yield 123
}
const f = foo()
const b = bar()
console.log(
f.next(),
f.next(),
b.next(),
b.next()
)
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
Much like a return
statement, an error thrown
inside the generator will make the generator finished — unless caught within the generator's body.
# The yield* directive delegates the execution to another generator
The yield*
directive delegates the execution to another generator.
yield* anotherGen
iterates over the generator anotherGen
and forwards its yields outside,
as if the values were yielded by the outer generator.
The result is the same as if we inlined the code from nested generators into the outer generator.
See the example hello-composition.js (opens new window)
# Delivery
Read both chapters and delivery a report like the one in ULL-MII-SYTWS-2021/learning-generators (opens new window)
# See
- ULL-MII-SYTWS-2021/learning-generators (opens new window) (campus-virtual/2021/learning/asyncjs-learning/learning-generators)
- Chapter Iterables (opens new window)
- Chapter Generators (opens new window) of JavaScript.info
- Stack overflow question (opens new window) JS Generators: How is
return yield
different fromyield
? - See the Node.js doc Modules: Packages (opens new window) for more details on the use of ECMA 6 Modules in Node.js.
- How Can I use an es6 Import in Node.JS (opens new window) Stackoverflow