What Promises Do That Observables Can’t


April 5, 2016
What Promises Do That Observables Can't

Nothing.

Observables are grabbing the spotlight as one of the cool new things Angular 2 is doing, despite having been around for some time. They are positioned to fully eclipse promises as the goto abstraction for dealing with async, among other things. Let’s dive into what Observables are and how they compare against promises in dealing with async data.

Promises and Observables

Promises are great. They were designed to be an answer to callback hell. Javascript’s tendency to keep edging to the right of the screen due to nesting has made many eyes bleed and brains explode. In comparison, the do this then this then that approach of promises was not only prettier and easier to read, it also provided a standard which could be embraced by developers to communicate with. Promises soon became the de-facto approach in most major frameworks and libraries. So much so in fact that the ES2015 specification incorporated promises as a core javascript API sans any third party library.

But promises act on data, and then return. They’re just sugar over the callback pattern. You can’t do much with a promise apart from getting a value or error out of it. How do we wrap a socket stream in a promise? Technically, it is never fulfilled. We just keep getting data out of it. We can use the progress method but it isn’t exactly meant for that and seems like a hack.

Enter Observables, the shiniest new abstraction for javascript devs. In reality though, Observables are simply the observer pattern at work. We’ve been attaching listeners to DOM events and reacting to events since the big bang. That pattern got abstracted to make it possible to interact with basically all data flow using the observer pattern. The resulting interface (or class/prototype/thing whatever you want to call it) was named Observable. Fitting.

Let’s see if we can reinforce this info by looking at a couple of examples.

/* This is how we've been doing observer pattern */
document.addEventListener('mousemove', e => console.log(e.clientX));

/* This is how the new Observable looks like doing same thing */
Rx.Observable.fromEvent(document, 'mousemove')
    .subscribe(e => console.log(e.clientX));

Why all the fuss, you ask? Because Observable doesn’t only work with DOM events. We can use it to deal with an amazing variety of data.

Rx.Observable.range(1, 10)
    .subscribe(e => console.log(e));

const sub = Rx.Observable.interval(1000)
    .subscribe(e => doThisEverySecond());
setTimeout(()=> sub.dispose(), 3000);

Notice how we’re using the same interface (subscribe) to deal with totally different types of operations (including the document listener we added in the previous example). This is one of the major strengths of using Observables. We don’t need to wire our brains differently. If the data can be thought of as evented, stream or async, we can wrap it in an Observable.

The abstraction of Observables is cool in itself. But that’s only one of the two things that make it awesome. The other half is the extensive Rx.js library itself. It has an amazing collection of methods (called operators) that can be employed to bend data to our will including some really handy utility methods. I’ll let the code do the talking.

/* Print 1 to 10 instantly then print a number every 2 seconds */
Rx.Observable.range(1, 10)
    .concat( Rx.Observable.interval(2000) )
    .subscribe(e => console.log(e));

/* Retry the Observable up to 2 times in case of error */
myHttpRequestObservable
    .retry(3) // 2 retries + 1 initial run
    .subscribe(e => console.log(e));

/* Print message if mouse moved within an area */
Rx.Observable.fromEvent(document, 'mousemove')
    .map(e => [e.clientX, e.clientY])
    .filter(e => isInArea(e)) // isInArea return boolean
    .subscribe(e => console.log('Mouse moved!'));

This is just the tip of the iceberg. Check out their repo for all operators available out of the box.

Observables in Angular 2

Angular 2 uses Rx.js Observables instead of promises for dealing with HTTP. This means, as we saw in the examples above, they come with some serious batteries included. That’s one of the reasons that HTTP operations in Angular 2 is so amazing. Here’s some code which consumes a quotes API via HTTP get. The result is an observable.

export class App {
  qlist: String[] = [];
  
  constructor (public http: Http) {
  }
  
  getQuote () {
    return this.http.get('http://quotesondesign.com/wp-json/posts?filter[orderby]=rand&filter[posts_per_page]=1');
  }
}

Note that since http.get method returns an Observable, merely calling the getQuote method won’t actually fire a request. An Observable starts emitting data when a subscriber is attached to it. Technically, this type of Observable is called a cold Observable. There’s also hot Observables that can emit data regardless of whether or not there are any subscriptions.

Now that we’ve seen how a simple request observable is defined in Angular 2, let’s see some operators on it. A code snippet is worth a thousand words:

class App {

   /* Existing methods … */

    addQuote () {
        this.getQuote()
            .retry(2) // in case of error, try 1 more time
            .repeat(3) // do this 3 times
            .map(res => res.json()) // convert response to json
            .filter(res => res.length > 0) // drop empty array responses
            .map(res => res[0].content.replace(/\<.*?\>/g, ''))
            .subscribe(quote => {
                this.qlist.push(quote);
            }, e => console.log(e.message));
    }
}

The above example could be found in action at this plunk.

Conclusion

Observables are powerful. And we’ve barely scratched the surface. They’re one of the proposed standards in ES2016. Looks like popular libraries are ending up in language specification these days :)

I’d love to hear your takeaways from this post and how you used observables in some new cool way. Any points for improving this post will be deeply appreciated as well. Use the comments section below or reach me on Twitter.


akash
Akash Agrawal is a Software Engineer with Modus Create. He is an experienced JavaScript and Ruby programmer with chops in front and backend development along with database architecture. Akash has a deep interest in pure functional languages and is a padwan haskell hacker. He enjoys exploring surrounding areas on his bike when not working.

  • Daniel Tiru

    One of the best fast descriptions i have seen so far. Thanks for sharing

  • http://about.me/eazyigz Igor Ganapolsky

    This is very fascinating. Is there any relation between JS Obervables and RxJava Observables? Do they support the same api’s?

    • http://akashagrawal.me/ Akash Agrawal

      I’m not following RxJava too closely to know of any possible divergences, but they should have same (or, at least very similar) API. Both RxJS and RxJava are two of the many platform/language wrappers for the polyglot project – Rx. Feel free to check out http://reactivex.io/languages.html for others.

  • Russell Dempsey

    I may be missing something, but I don’t feel like you ever answered the question “What Promises Do That Observables Can’t”

    • http://akashagrawal.me/ Akash Agrawal

      This is a teaser to demo the capabilities of observables over promises. I haven’t come across a use case where I’d thought, ‘I should have used promises here instead of observables’. Hence, the first word of the article: ‘Nothing’.

  • MMI

    Great article!


What We Do

We’ll work closely with your team to instill Lean practices for ideation, strategy, design and delivery — practices that are adaptable to every part of your business.

See what Modus can do for you.

LET'S GET STARTED

We're Hiring!

Join our awesome team of dedicated engineers.

Loading...

APPLY NOW