Observables vs Promises in JavaScript

In modern web development, managing asynchronous operations efficiently is a crucial task. Two widely used tools in JavaScript for handling asynchronous actions are Promises and Observables. This blog will explore the differences between Observables vs Promises, how they work, and when to use each in your JavaScript projects.

While Promises have been around for quite some time, Observables, introduced by libraries like RxJS (Reactive Extensions for JavaScript), have gained popularity due to their flexibility and power.


Promises are objects that represent the eventual completion or failure of an asynchronous operation and its resulting value. They are native to JavaScript and follow a straightforward, one-time asynchronous data-handling approach.

Characteristics of Promises:

  • One-time Execution: A promise represents a single asynchronous operation. Once it resolves or rejects, it’s done.
  • Eager Execution: Promises execute immediately once created.
  • Immutable State: A promise has three states: pending, fulfilled, or rejected. Once it moves from pending to either fulfilled or rejected, its state can no longer change.
  • Chaining: Promises use .then() and .catch() to handle success or failure, allowing for chaining of multiple asynchronous actions.

Example of a Promise:

const fetchData = new Promise((resolve, reject) => {
  setTimeout(() => {
    resolve("Data fetched successfully!");
  }, 2000);
});

// Using the promise
fetchData
  .then(response => console.log(response))
  .catch(error => console.error(error));

In this example, fetchData is a promise that resolves after 2 seconds, simulating data fetching. The then() block is used to handle the resolved value, while catch() is used to handle any errors.


Observables, provided by libraries like RxJS, are a more advanced concept used to handle streams of asynchronous data over time. Unlike promises, observables can handle multiple values, push data as it becomes available, and allow for rich manipulation of asynchronous data streams.

Characteristics of Observables:

  • Multiple Values: An observable can emit multiple values over time, unlike a promise that resolves only once.
  • Lazy Execution: Observables are lazy, meaning they don’t start emitting values until you subscribe to them.
  • Cancellable: You can unsubscribe from an observable to stop receiving data, which is useful for managing resources in complex applications.
  • Functional Operators: Observables support a wide range of operators like map, filter, reduce, merge, and more, enabling fine-grained control over how data streams are processed.
  • Reactivity: Observables promote reactive programming, where your application can react to changes in data streams efficiently.

Example of an Observable:

import { Observable } from 'rxjs';

// Creating an observable
const dataStream = new Observable(subscriber => {
  subscriber.next('First data emitted');
  setTimeout(() => {
    subscriber.next('Second data emitted after 2 seconds');
  }, 2000);
  setTimeout(() => {
    subscriber.complete(); // Signals the end of the observable
  }, 3000);
});

// Subscribing to the observable
dataStream.subscribe({
  next(value) { console.log(value); },
  complete() { console.log('Observable complete'); }
});

In this example, the observable dataStream emits two pieces of data at different times. The next() function sends values to the subscriber, and complete() signals that no more values will be emitted.


FeaturePromisesObservables
Handling Multiple ValuesCan only handle one value or errorCan handle multiple values over time
Eager vs LazyEager: Executes immediatelyLazy: Executes only when subscribed
CancellabilityCannot be cancelled once startedCan be cancelled by unsubscribing
OperatorsLimited operators (then, catch, finally)Rich set of operators (map, filter, merge, etc.)
ReactivityNot inherently reactiveReactive by design (push-based)
StateOne-time resolution (fulfilled/rejected)Continuous data emission until complete

Promises are simpler to use for single asynchronous operations. They are ideal for situations where you need to perform an asynchronous task and handle its result just once.

Here are some common use cases for promises:

  • API calls: When making a single HTTP request to fetch or send data.
  • File I/O operations: Reading or writing a file asynchronously.
  • Authentication: Checking login credentials and handling the result.
  • Basic animations: Triggering an animation once and handling its completion.

Example of a Simple API Call Using Promises:

fetch('https://api.example.com/data')
  .then(response => response.json())
  .then(data => console.log(data))
  .catch(error => console.error('Error fetching data:', error));

In this scenario, a promise is perfect because the API request completes once, and you handle either the result or the error.


Observables shine when you need to handle continuous or multiple cccevents. They are more powerful than promises in scenarios that involve real-time data or multiple values over time.

Here are some cases where observables are a better fit:

  • Event Streams: Handling streams of events, such as user input (e.g., clicks, key presses).
  • WebSocket Connections: Handling data coming from WebSocket connections in real time.
  • Live Data Streams: Fetching real-time data, such as stock prices or sensor readings.
  • Complex Asynchronous Workflows: When you need to combine, merge, or transform multiple asynchronous events.

Example of an Observable for WebSocket:

import { webSocket } from 'rxjs/webSocket';

const stockPriceSocket = webSocket('wss://example.com/stock-price');

// Subscribing to the WebSocket
const subscription = stockPriceSocket.subscribe({
  next(price) { console.log(`Stock price updated: ${price}`); },
  error(err) { console.error(`WebSocket error: ${err}`); },
  complete() { console.log('WebSocket connection closed'); }
});

// Unsubscribing when we no longer need updates
setTimeout(() => {
  subscription.unsubscribe();
  console.log('Stopped receiving stock price updates');
}, 10000);

In this case, an observable is ideal because it handles a continuous stream of stock price updates from a WebSocket.


The decision between promises and observables depends on the needs of your project. If you’re dealing with one-time asynchronous tasks, promises are simpler and more straightforward to use. However, for handling continuous streams of data or complex asynchronous workflows, observables offer far greater flexibility and control.

Here’s a quick rule of thumb:

  • Use Promises for single asynchronous actions (e.g., API calls, file handling).
  • Use Observables when you need to work with data streams, multiple asynchronous events, or handle complex reactivity.

By understanding the strengths of both tools, you can choose the right approach for your project and ensure that your asynchronous code is efficient, maintainable, and scalable.


1. What is the main difference between Observables and Promises?

Observables handle multiple values over time, while Promises deal with a single resolved or rejected value. Observables are also lazy (start on subscription), whereas Promises are eager.

2. Can you convert a Promise into an Observable?

Yes, you can easily convert a Promise into an Observable using libraries like RxJS, which offer a method like fromPromise() for this purpose.

3. Do Observables cancel their execution?

Yes, Observables can be unsubscribed from, stopping further emissions and canceling execution, whereas Promises cannot be canceled once initiated.

4. Are Observables part of JavaScript by default?

No, Observables are not natively part of JavaScript; they are commonly used via external libraries like RxJS, while Promises are built into JavaScript.

5. Is RxJS required to use Observables?

Yes, RxJS is the most popular library for using Observables in JavaScript. Observables are not built into JavaScript, so you need RxJS or a similar library to implement them.


Further Reading:

Leave a Comment