Difference between Observable and Promise | with examples

Last modified: September 3, 2019

ES6 Promises and RxJS Observables are both used to handle async activity in JavaScript. While an Observable can do everything a Promise can, the reverse is not true...

What is a Promise?

A Promise is a more elegant way of handling async activity in JavaScript.

Promises are native to ES6 meaning you can use them with vanilla JavaScript (assuming you are using ES6 version or later).

Example of a Promise:

let promise = new Promise((resolve) => {
  setTimeout(() => {
    resolve("some value")
  }, 1000`)
})

promise.then(value => {
  console.log(value)
})

Output

some value

A Promise works by taking a function with (optionally) two arguments resolve and reject. The passed in function executes some async code and either resolves or rejects.

Once the Promise resolves, its then() method executes a handler function with the emitted value.

This is a very basic example of a Promise. For a more in-depth explanation with examples, check out JavaScript ES6 Promises.

What is an Observable?

Observables (in the JavaScript context) are the RxJS implementation of the observer design pattern.

Observables are an alternative to Promises for handling async activity:

Example of an Observable:

const { Observable } = require('rxjs');

let observable = new Observable((observer) => {
  setTimeout(() => {
    observer.next("some value")
  }, 1000)
})

observable.subscribe(value => {
  console.log(value)
})

Output

some value

Unlike the Promise, Observables aren't native to JavaScript. This requires us to import the RxJS library:

const { Observable } = require('rxjs');

While you can implement the observer design pattern yourself, the "Observable" in JavaScript usually refers to the RxJS implementation.

For more information on Observables in JavaScript, check out JavaScript Observables in 5 Minutes.

Difference Between Observables and Promises:

Single vs Multiple Values:

A Promise emits a single value:

let promise = new Promise((resolve) => {
  resolve("a")
  resolve("b")
})

promise.then(value => console.log(value))

Output

a

An Observable can emit multiple values:

let observable = new Observable((observer) => {
  observer.next("a")
  observer.next("b")
})

observable.subscribe(value => {
  console.log(value)
})

Output

a
b

Once a Promise resolves, that's it. You can't call resolve twice and get different values. Only the first value a is returned by the promise.

An Observable can emit multiple values. Calling next() executes the handler function each time. Both a and b are returned.

Eager vs Lazy Execution:

A Promise executes the moment it is defined:

let promise = new Promise((resolve) => {
  console.log("promise is running")
  resolve("a")
})

console.log("start")
promise.then(value => console.log(value))
console.log("end")

Output

promise is running
start
end
a

An Observable executes only when subscribe() is called:

let observable = new Observable((observer) => {
  console.log("observable is running")
  observer.next("a")
})

console.log("start")
observable.subscribe(value => console.log(value))
console.log("end")

Output

start
observable is running
a
end

A Promise executes the moment it's defined. Even if we don't call then(), the Promise executes. This is considered eager execution.

An Observable executes only when subscribe() is called. If we don't call subscribe(), nothing executes. This is considered lazy execution.

Cancellable:

A Promise can't be canceled. That's it. No example needed.

An Observable can be canceled via unsubscribe():

let observable = new Observable((observer) => {
  setTimeout(() => {
    console.log("calling next")
    observer.next("a")
  }, 1000)
})

sub = observable.subscribe(value => console.log(value))
sub.unsubscribe()

Output

calling next

Notice how we use setTimeout() to mimic async activity. By calling unsubscribe() on our subscription, you can effectively cancel an Observable before it executes.

Unicast vs Multicast:

A Promise is unicast meaning you get the same value every time you call the async flow:

let promise = new Promise((resolve) => {
    resolve(Math.random())
})

promise.then(value => console.log(value))
promise.then(value => console.log(value))

Output

0.768598539600432

An Observable is multicast meaning a separate execution occurs for every call to subscribe:

let observable = new Observable((observer) => {
  observer.next(Math.random())
})

observable.subscribe(value => console.log(value))
observable.subscribe(value => console.log(value))

Output

0.6964325798899575
0.5931491554914805

With a Promise we get the same value no matter how many times we call then().

With an Observable we get a different value every time we call subscribe().

Sync vs Async Handlers:

A Promise has async handlers. This means a Promise won't execute synchronously with the rest of your code:

let promise = new Promise((resolve) => {
    resolve("promise is resolving")
})

console.log("START")
promise.then(value => console.log(value))
console.log("END")

Output

START
END
promise is resolving

An Observable has sync handlers. This means an Observable WILL execute synchronously with the rest of the code:

let observable = new Observable((observer) => {
  observer.next("next being called")
})

console.log("START")
observable.subscribe(value => console.log(value))
console.log("END")

Output

START
next being called
END

Notice how the Promise executes asynchronously from the rest of the code. The code doesn't wait for the Promise to run.

The Observable executes synchronously with the rest of the code. The code waits for the Observable to emit a value before continuing.

Promise vs RxJS

Remember that the Observable class is from the RxJs library. The RxJS library is the JavaScript implementation of ReactiveX.

ReactiveX is a project for implementing reactive programming across different languages. You can implement Observables in other languages like Java and Ruby as well.

The ReactiveX API also comes with a bunch of operators which are functions that can be applied to observables. Using RxJS, you can apply functions like map() and filter() to values emitted by observables.

The ES6 Promise doesn't have anything comparable to these operator functions...

Conclusion

Promises and Observables both handle async activity in JavaScript. While the Promise is native to ES6, the RxJS Observable requires the RxJS library.

Observables can do things promises can't. With observables, you get some extra advantages that can be worth while depending on your use case.

You might also like: