What is the difference between Promises and Observables?

Highest praise: 1777

A Promise handles a single event when an asynchronous operation completes or fails.

Note: There is Promise library support for the Cancellation operation, but ES6 Promise does not support it so far.

Observable

An Observable is like a Stream (in many languages), allowing zero or more events to be passed, with callbacks invoked for each event.

Often, An Observable is more popular than a Promise because it provides the features of a Promise, etc. With An Observable, it doesn’t matter whether you’re handling 0, 1, or more events. You can use the same API in each case.

Observable also has the advantage of cancellations over Promise. The Observable subscription allows unsubscription if an HTTP request to the server or some other expensive asynchronous operation is no longer needed, and the Promise will eventually call a success or failure callback, even if you don’t do so and no longer need the notification or the result it provides.

While a Promise starts immediately, an Observable only starts when you subscribe to it. This is why Observable is called lazy.

Observable provides map, forEach, reduce, and other operators that are used similarly to arrays.

There are also powerful operators, such as retry() or replay(), which are often very convenient.

Delayed execution allows for more declarative programming by establishing a set of operators before an Observable is executed by subscription.

# 2:374 likes

Give me an example.

Angular uses rX.js Observables instead of promises to handle HTTP.

Suppose you’re building a search feature that should display results immediately as you type. It sounds familiar, but this task brings many challenges.

We don’t want to have to access the server endpoint every time the user presses a key; if we do, the server will be flooded with HTTP requests. Basically, we just want the HTTP request to fire when the user stops typing, not every keystroke.

Do not access the search endpoint with the same query parameters for subsequent requests.

Processing unordered responses. When we have multiple requests going on at the same time, we have to consider the case where they return in an unexpected order. Imagine we type computer, stop, make a request, and then car, stop, make a request. Now we have two requests in progress. Unfortunately, requests that carry results to computer are returned after requests that carry results to CAR.

First look at how to implement this requirement with a Promise. Of course, none of the above mentioned boundary cases are handled.

Wikipedia – service. Ts:

import { Injectable } from '@angular/core'; import { URLSearchParams, Jsonp } from '@angular/http'; @Injectable() export class WikipediaService { constructor(private jsonp: Jsonp) {} search (term: string) { var search = new URLSearchParams() search.set('action', 'opensearch'); search.set('search', term); search.set('format', 'json'); return this.jsonp .get('http://en.wikipedia.org/w/api.php?callback=JSONP_CALLBACK', { search }) .toPromise() .then((response) => response.json()[1]); }}

We are injecting the Jsonp service to make GET requests to the Wikipedia API with a given search term. Note that we call toPromise to go from Observable<Response> toPromise <Response>. We end up with Promise<Array<string>> as the return type of our search method.

Implementation of app.ts:

// check the plnkr for the full list of imports import {... } from '... '; @Component({ selector: 'my-app', template: ` <div> <h2>Wikipedia Search</h2> <input #term type="text" (keyup)="search(term.value)"> <ul> <li *ngFor="let item of items">{{item}}</li> </ul> </div> ` }) export class AppComponent { items: Array<string>; constructor(private wikipediaService: WikipediaService) {} search(term) { this.wikipediaService.search(term) .then(items => this.items = items); }}

There are no surprises here. We inject our WikipediaService and expose its functionality to the template through the search method. The template simply binds to keyup and calls search(term.value).

We unpack the results of the Promise returned by the search method for WikipediaService and expose it to the template as a simple array of strings so that we can have the *ngFor loop iterate over it and build a list for us.

Where Observables really shine

Let’s change our code so that instead of hitting the endpoint with each keystroke, we only send the request 400 milliseconds after the user stops typing

To reveal such a superpower, we first need to get an Observable< String > that carries the search terms entered by the user. Instead of binding to the KeyUp event manually, you can use Angular’s formControl directive. To use this directive, we first need to import ReactiveFormsModule into our application module.

app.ts:

import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { JsonpModule } from '@angular/http';
import { ReactiveFormsModule } from '@angular/forms';

@NgModule({
  imports: [BrowserModule, JsonpModule, ReactiveFormsModule]
  declarations: [AppComponent],
  bootstrap: [AppComponent]
})
export class AppModule {}

After importing, we can use formControl in the template and set it to the name “term”.

<input type="text" [formControl]="term"/>

In our component, we create an instance of FormControl from @angular/form and expose it as a field under the name term on the component.

Behind the scenes, term automatically exposes an Observable<string> as a property, valueChanges, to which we can subscribe. Now that we have an Observable<string>, getting user input is as simple as calling debounceTime(400) on our Observable. This returns a new Observable<string> that emits a new value only if no new value appears for 400 milliseconds.

export class App { items: Array<string>; term = new FormControl(); constructor(private wikipediaService: WikipediaService) { this.term.valueChanges .debounceTime(400) // wait for 400ms pause in events .distinctUntilChanged() // ignore if next search term is same as previous .subscribe(term => this.wikipediaService.search(term).then(items => this.items = items)); }}

It would be a waste of resources to make another request for a search term for which our application has already shown results. To implement the required behavior, all we have to do is call the distinctUntilChanged operator immediately after we call debounceTime(400).

Observable and Promise compare:

More of Jerry’s original articles can be found in “Wang Zixi “: