JavaScript
Generators and Iterables
5/25/2023
Generators and Iterable are two important concepts in JavaScript, which are used to handle data structures in a more efficient way.
Iterables
An Iterable is an object that can be iterated over using the for...of
loop. An iterable object must have a method named Symbol.iterator
that returns an iterator object. For example, the Array
object is iterable because it has the Symbol.iterator
method, which returns an iterator object that can be used to loop over the array.
const arr = [1, 2, 3, 4]
const iterator = arr[Symbol.iterator]()
for (const element of iterator) {
console.log(element)
}
// Output: 1 2 3 4
Generators
Generators are functions that can be paused and resumed during their execution. They are defined using the function*
syntax and use the yield
keyword to pause their execution and return a value. When a generator is called, it returns an iterator object that can be used to control its execution.
function* generator() {
yield 1
yield 2
return 3
}
const gen = generator()
console.log(gen.next()) // { value: 1, done: false }
console.log(gen.next()) // { value: 2, done: false }
console.log(gen.next()) // { value: 3, done: true }
Generators can also receive values from outside using the next()
method. The value passed to next()
becomes the value returned by the yield
keyword.
function* generator() {
const x = yield "input x"
const y = yield x + 2
yield y + 3
}
const iterator = generator()
console.log(iterator.next().value) // input x
console.log(iterator.next(4).value) // 6 (4 + 2) 4 goes to x and yield x + 2
console.log(iterator.next(5).value) // 8 (5 + 3)
Generators are iterable
Generators are iterable. So they can be used in for...of
loop.
function* generator() {
yield 1
yield 2
return 3
}
const gen = generator()
for (const value of gen) {
console.log(value)
}
// Output
1
2
Note: the for...of
loop ignores the last value
when done: true
.
Since generators are iterable, you can also use the spread syntax.
function* generator() {
yield 1
yield 2
return 3
}
const gen = generator()
const arr = [0, ...gen]
console.log(arr) // [0, 1, 2]
And it’s also useful when you create your own Symbol.iterator
method.
const iterableObject = {
length: 5,
*[Symbol.iterator]() {
for (let i = 0; i < this.length; i++) {
yield i
}
},
}
console.log([...iterableObject]) // [0, 1, 2, 3, 4]
Generator.return(value)
This method finishes the generator’s execution and return the given value
.
function* generator() {
yield 1
yield 2
return 3
}
const gen = generator()
console.log(gen.next()) // { value: 1, done: false }
console.log(gen.return(-1)) // { value: -1, done: true }
console.log(gen.next()) // { value: undefined, done: true }
Generator.throw(error)
This method passes an error to a yield
keyword.
function* generator() {
yield 1
console.log("Not reachable log")
yield 2
return 3
}
const gen = generator()
console.log(gen.next()) // { value: 1, done: false }
console.log(gen.throw("TL")) // Uncaught TL
console.log(gen.next()) // ignored
Generators are useful when dealing with large or infinite data structures, as they allow lazy evaluation of the data. They can also be used to implement custom iteration protocols and control the flow of asynchronous code.
Conclusion
Generators and Iterable are two powerful features of JavaScript that can be used to handle data structures in a more efficient and flexible way. They are widely used in modern JavaScript frameworks and libraries and are an essential part of any developer’s toolkit.