Iterable protocol
Protocol characteristics
The
“Iterable protocol” is an agreed-upon APi at the level of JavaScript as a language, for creating objects that can be used to iterate over a collection of data. The key characteristic of this protocol is “sequentiality” ie. property that when iterating, it returns the value of the iterable structure one by one. Data types that satisfy the iterable protocol are called “iterable collections”. Respecting the iterable protocol, it is necessary that the function returns an object containing a special method “next()”.
next()
When called, the next() method returns a member of the collection temporarily “wrapped” (eng. wrapped”) with an object that has two properties: value and done. As long as there are members for iteration, the method returns the value of the “done” property as “false”, and only when the iteration “comes” to the end, the “done” property is defined as “true”.
Example
Through the following example, the concept of the protocol:
is described
|
1 2 3 4 5 6 7 8 9 10 11 12 13 |
function arrayIterator(array) { var i = 0; return { next: function() { return i < array.length ? { value: array[i++], done: false } : { done: true }; } } } |
Which data types are iterable?
It should be emphasized that ordinary javascript objects are not iterable, but the following data types satisfy the iterable protocol:
- String
- Array
- Array-like arguments or NodeList object
- TypedArray
- Map
- Set
Which mechanisms in JavaScript require an iterable collection?
Mechanisms for the use of which “iterable” collections are necessary are:
- Destructuring
- for..of loop
- Array.from()
- Spread operator (…)
- Maps & Sets
- Promise.all(), Promise.race()
- yield*
Example
Iterable collections can use mechanisms that require iterable collections such as iteration with a “for..of” loop:
|
1 2 3 4 |
const array = [1, 2, 3, 4]; for(let elem of array) { console.log(elem); } |
or when destructing:
|
1 2 3 4 5 6 7 |
const nekaMapa = new Map([['key1', 1], ['key2', 2], ['key3', 3]]); for(let [key, value] of nekaMapa) { console.log(`${key}: ${value}`); } |
Iterable and Iterator
Creating an iterator with an iterable object
To create an iterator, the object needs to have a specific internal method that returns an iterator, whose key is “Symbol.iterator”. Such an object is called an “Iterable object”.
|
1 2 3 4 5 6 7 |
{ [Symbol.iterator]() { // The body of a function that returns an iterator object } } |
Iterator object is created by calling the iterable object method named: “[Symbol.iterator]( )”.
|
1 |
const noviIterator = nekiIterableTipPodataka[Symbol.iterator](); |

Definition
Iterator is an object, which knows how to access the members of the iterable collection, one by one and contains a pointer that points to the next element. The iterator object is provided by “next()” method, which returns a “wrapped” collection member with an object that has two properties: value and done.
Example
|
1 2 3 4 5 6 7 8 9 10 11 |
// Iterabilna struktura podataka const someArray = ['a', 'b', 'c']; // Creating an Iterator object with an iterable method using the "Symbol.iterator" key const nekiIterator = someArray[Symbol.iterator](); // Access to members of the itrable collection nekiIterator.next() // { value: 'a', done: false } nekiIterator.next() // { value: 'b', done: false } nekiIterator.next() // { value: 'c', done: false } nekiIterator.next() // { value: undefined, done: true } |
As you can see from the previous example, the property “done” signals when the iteration has come to an end.
Application of Iterable and Iterator
Thanks to the iterator and the next() method, iteration through the iterable collection for for..of loop is enabled:
|
1 2 3 4 5 6 7 8 9 10 11 12 |
// Iterabilna struktura podataka const someArray = ['a', 'b', 'c']; // Creating an Iterator object with an iterable method using the "Symbol.iterator" key const nekiIterator = someArray[Symbol.iterator](); for (let n of nekiIterator) { console.log(n) } // a // b // c |
Generator function and Generators
Marking the generator function
A generator function is a special type of function that is denoted as follows:
|
1 2 3 |
function* nazivFunkcije (){ ... } |
A generator function can also be defined as a object method:
|
1 2 3 4 5 |
const obj = { * generatorMethod() { ··· } }; |
or even as a class method:
|
1 2 3 4 5 |
class MyClass { * generatorMethod() { ··· } } |
Characteristics of the generator function
The generator function acts as a “factory” for specific so-called iterators. generators, and in addition it can pause its work with the keyword “yield”. When the compiler “runs into” the “yield” keyword during the execution of the function, it pauses the further execution of the function and returns a generator object that was created according to the “iterable protocol” (iterable). The keyword “yield” is allowed to be used only within the generator. To continue the execution of the generator function, the iterator method “next()” is applied.

Generators

Generator is a specific type of iterator, which is created by calling the generator function. The generator function generates an iterable dataset, so we can use the next() method as well as the “for..of” loop to iterate. The differences between a “regular” iterator and a generator are:
- with the generator, the method can receive a parameter gen.next(value)
- there is an additional method throw()
As with iterators, the “next()” method returns a collection member “wrapped” with an object that has two properties: value and done. The value of the property can be obtained directly from yield or as the returned value of the generator parameter. The “yield” keyword always returns some value even if it is null.
“Generators have a built-in communication channel with yield”
|
1 2 3 4 5 6 7 |
function* channel () { var name = yield 'Hello, what is your name?' return 'Zdravo, ja sam ' + name } var gen = channel() console.log(gen.next().value) // Hello, what is your name? (currently yielded value from the generator) console.log(gen.next('Pera')) // Hello, I'm Pera (here the passed value replaced the previous yield value) |
Example
In this example, after the execution of the first line, the keyword “yield” pauses further execution of the function. To continue the function, the “next()” method is used, after which the method returns the next member of the “wrapped” collection with an object that has two properties: value and done.
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
// Generator function: function* numbers () { yield 1; yield 2; return 3; } // The following code does not execute the *numbers() generator function, but creates a generator (specific iterator) var nekiGenerator = numbers(); // Continuing iteration with the next() method: console.log(nekiGenerator.next()); // Runs the generator reads the first line and returns: { done: false, value: 1 } console.log(nekiGenerator.next()); // Returns: { done: false, value: 2 } console.log(nekiGenerator.next()); // Returns: { done: true, value: 3 } |
Thanks to the feature of the generator function that can be paused during execution and then continue execution, only with generators and generator functions it is possible to stop the execution of one function, start another that returns a value, and send that value back through a parameter to the first function and continue execution.
Example
|
1 2 3 4 5 6 7 8 9 10 11 12 |
function* logGenerator() { console.log("pocetak"); console.log(1, yield); console.log(2, yield); console.log(3, yield); } var gen = logGenerator(); gen.next(); // pocetak gen.next('Pera'); // 1 Pera gen.next('Mika'); // 2 Mika gen.next('Zika'); // 3 Zika |
Example
Thanks to the possibility that a function can stop its execution, we can then call another function as in this example:
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
var x=1; function* generatorskaFunkcija (){ console.log("x: ", x); yield; // Pauziranje console.log("x: ", x); } function someFunction () { x++; } // Constructing a new iterator used to control the generator var nekiGenerator = generatorskaFunkcija(); // Starting the generator function nekiGenerator.next(); // Returns: x:1 // We call another function in the middle of the execution of the generator function someFunction(); // x=2 someFunction(); // x=3 // Continuation of execution of the generator function nekiGenerator.next(); // Returns: x: 3 |
