Using ES6 Generators As Observers

Swarup Karavadi
2 min readMay 11, 2017

Prereq: Some familiarity with FRP libraries like RxJS would be helpful.

Typically, an observer has three methods — next, error, completed. A simple observer can look like —

const observer = {
next(value) { console.log(`next -> ${value}`) },
error(err) { console.log('error') },
complete() { console.log('completed') }
}

An observable would look something like this —

function observable(observer) {
for(var i = 0; i <= 10; i++) {
observer.next(i)
}
observer.error()
observer.complete()
}

This observable emits a sequence of numbers from 0 to 10, then emits an error and finally emits a complete. This is not a realistic implementation. In the real world, we have the concept of SafeObservers where the observable stops getting observed the moment either one of error or complete are emitted. But the point of this article is not to get into real world observers and observables but to show you how an ES6 generator can be used as an observer.

It doesn’t suffice if we have an observable and an observer — the observer needs to observe the observable. This is as simple as —

observable(observer)

Resulting in this —

"next -> 0" 
"next -> 1"
"next -> 2"
"next -> 3"
"next -> 4"
"next -> 5"
"next -> 6"
"next -> 7"
"next -> 8"
"next -> 9"
"next -> 10"
"error"
"completed"

As I was mentioning above, an observer has three methods — next, error and close. If you look at an ES6 iterator (that is usually created by invoking a generator), it also has three methods — next, throw and return. So a you can create a generator that returns an iterator like this —

function* observerGenerator() {
try {
while(true) {
let value = yield
console.log(`next -> ${value}`)
}
} catch (err) {
console.log('error')
}

console.log('completed')
}

We then create a utility function that converts our iterator into an observer -

function createObserver(iterator) {
return {
next(value) { iterator.next(value) },
error(err) { iterator.throw(err) },
complete() { iterator.return() }
}
}

Notice the mapping between observer methods and iterator methods —

|    Observer    |    Iterator    |
-----------------------------------
| next | next |
| error | throw |
| complete | return |

To start observing the observable —

observable(createObserver(observerGenerator()))

And the result is the same as before. The code snippets are available as a gist here.

--

--