Javascript has a number of iteration tools for-loop, iterator pattern and Generators which i will cover in this section.
The for-loop is the traditional tool for iteration, however Javascript does have some others like the iterator pattern which I am going to cover.
| Basic for-loop example | for (let i = 1; i <= 10; ++i) {
console.log(i);
}
let collection = ['foo', 'bar', 'baz'];
for (let index = 0; index < collection.length; ++index) {
console.log(collection[index]);
}
|
| Collection forEach() example | let collection = ['foo', 'bar', 'baz']; collection.forEach((item) => console.log(item)); |
Iterator pattern is one of the design patterns from the gang of four, it describes something that is iterable and can inplement a formal Iterable interface and be consumed by a Iterator. The Object created would return a Iterator that would be able to iterate over the elements inside the Object. Built-in objcts likes Strings, Arrays, Maps/Sets already have a iterator.
| Check existence of iterator | // These types do not have iterators
let num = 1;
let obj = {};
console.log(typeof num[Symbol.iterator] === 'function'); // false
console.log(typeof obj[Symbol.iterator] === 'function'); // false
// These types all have iterators
let str = 'abc';
let arr = ['a', 'b', 'c'];
let map = new Map().set('a', 1).set('b', 2).set('c', 3);
let set = new Set().add('a').add('b').add('c');
console.log(typeof str[Symbol.iterator] === 'function'); // true
console.log(typeof arr[Symbol.iterator] === 'function'); // true
console.log(typeof map[Symbol.iterator] === 'function'); // true
console.log(typeof set[Symbol.iterator] === 'function'); // true |
| Iterators in action | // for...of loops
let arr = ['foo', 'bar', 'baz'];
for (let ele of arr) { // foo, bar, baz
console.log(ele);
}
// Array destructuring
let [a, b, c] = arr;
console.log(a, b, c); // foo, bar, baz
// Spread operator
let arr2 = [...arr];
console.log(arr2); // ['foo', 'bar', 'baz']
// Array.from()
let arr3 = Array.from(arr);
console.log(arr3); // ['foo', 'bar', 'baz']
// Set constructor
let set = new Set(arr);
console.log(set); // Set(3) {'foo', 'bar', 'baz'}
// Map constructor
let pairs = arr.map((x, i) => [x, i]);
console.log(pairs); // [['foo', 0], ['bar', 1], ['baz', 2]]
let map = new Map(pairs);
console.log(map); // Map(3) { 'foo'=>0, 'bar'=>1, 'baz'=>2 } |
| Obtain and Use a Iterator | let names = ["Paul", "Will", "Moore", "Graham"]
let iter = names[Symbol.iterator](); // obtain the iterator
console.log(iter.next()); // { value: 'Paul', done: false }
console.log(iter.next()); // { value: 'Will', done: false }
console.log(iter.next()); // { value: 'Moore', done: false }
console.log(iter.next()); // { value: 'Graham', done: false }
console.log(iter.next()); // { value: undefined, done: true } - done is now true, no more elements |
| Custom Iterator | class Counter {
// Counter instance should iterate <limit> times
constructor(limit) {
this.count = 1;
this.limit = limit;
}
next() {
if (this.count <= this.limit) {
return { done: false, value: this.count++ };
} else {
return { done: true, value: undefined };
}
}
reset() {
this.count = 1; // reset the count back to one
}
[Symbol.iterator]() {
return this;
}
}
let counter = new Counter(3);
for (let i of counter) {
console.log(i);
}
counter.reset(); // need to reset the the count back to 1
console.log(counter.next());
console.log(counter.next());
console.log(counter.next());
console.log(counter.next());
Output
--------------------------------------------------
1
2
3
{ done: false, value: 1 }
{ done: false, value: 2 }
{ done: false, value: 3 }
{ done: true, value: undefined } |
Generators allow you to pause and resume code execution inside a function block, below is a how they work

Generators that the form of a function, and use a asterisk to indicate its a generator, they implement an Iterator interface which means they have a next() method, which begins or resumes the execution, the yield keyword allows the generator to stop and start execution, when the generator encounters the yield keyword the execution will halt and the scope of the function will be preserved, it will only start again then next() is called.
| Basic Generator | function* generatorFn() { // notice the asterisk
console.log("foo");
yield 'foo';
console.log("bar");
yield 'bar';
console.log("baz");
return 'baz';
}
let generatorObject1 = generatorFn();
let generatorObject2 = generatorFn();
console.log(generatorObject1.next()); // { done: false, value: 'foo' }
console.log(generatorObject2.next()); // { done: false, value: 'foo' }
// At this point we are positioned at console.log("bar") ready to continue
console.log(generatorObject1.next()); // { done: false, value: 'bar' }
console.log(generatorObject2.next()); // { done: false, value: 'bar' }
// At this point we are positioned at console.log("baz") ready to continue
console.log(generatorObject1.next()); // { done: false, value: 'baz' }
console.log(generatorObject2.next()); // { done: false, value: 'baz' }
Output
-----------------------------------------------------------------
foo
{ value: 'foo', done: false }
foo
{ value: 'foo', done: false }
bar
{ value: 'bar', done: false }
bar
{ value: 'bar', done: false }
baz
{ value: 'baz', done: true }
baz
{ value: 'baz', done: true } |
| Generator example | // Infinite counting generator function, util program restarts
function* generatorFn() {
for (let i = 0;;++i) {
yield i;
}
}
let generatorObject = generatorFn();
console.log(generatorObject.next().value); // 0
console.log(generatorObject.next().value); // 1
console.log(generatorObject.next().value); // 2
console.log(generatorObject.next().value); // 3
console.log(generatorObject.next().value); // 4
console.log(generatorObject.next().value); // 5 |
| Yielding an Iterable | // generatorFn is equivalent to:
// function* generatorFn() {
// for (const x of [1, 2, 3]) {
// yield x;
// }
// }
function* generatorFn() {
yield* [1, 2, 3]; // Notice the asterisk
}
let generatorObject = generatorFn();
for (const x of generatorFn()) { // results in 1, 2, 3
console.log(x);
} |
| Returning early | function* generatorFn() {
for (const x of [1, 2, 3]) {
yield x;
}
}
const g = generatorFn();
console.log(g); // generatorFn {<suspended>}
console.log(g.return(4)); // { done: true, value: 4 }
console.log(g); // generatorFn {<closed>}
Note: you can also use throw() |