DEV Community

Cover image for Observables vs Promises
Shaikh AJ
Shaikh AJ

Posted on

Observables vs Promises

Asynchronous programming is a cornerstone of modern web development, enabling applications to handle tasks such as data fetching, event handling, and real-time updates without blocking the main execution thread. Two powerful tools for managing asynchronous operations in JavaScript are Observables and Promises. This article delves into the differences between Observables and Promises, providing clear examples to help you understand when and how to use each.

Table of Contents

  1. Introduction to Promises
  2. Introduction to Observables
  3. Key Differences Between Promises and Observables
  4. Examples
  5. When to Use Promises vs. Observables
  6. Conclusion

Introduction to Promises

Promises are a modern JavaScript feature designed to handle asynchronous operations. A Promise represents a single value that will be available now, in the future, or never. It can be in one of three states: pending, fulfilled, or rejected.

Creating a Promise

const myPromise = new Promise((resolve, reject) => {
  setTimeout(() => {
    resolve('Promise resolved!');
  }, 1000);
});
Enter fullscreen mode Exit fullscreen mode

Consuming a Promise

myPromise.then(value => {
  console.log(value);
}).catch(error => {
  console.error(error);
});
Enter fullscreen mode Exit fullscreen mode

Output

Promise resolved!
Enter fullscreen mode Exit fullscreen mode

Introduction to Observables

Observables are a key feature of the Reactive Extensions (RxJS) library, used extensively in frameworks like Angular. An Observable is a stream of data that can emit multiple values over time. Unlike Promises, Observables are lazy and can be canceled.

Creating an Observable

import { Observable } from 'rxjs';

const myObservable = new Observable(observer => {
  setTimeout(() => {
    observer.next('First value');
  }, 1000);

  setTimeout(() => {
    observer.next('Second value');
  }, 2000);

  setTimeout(() => {
    observer.complete();
  }, 3000);
});
Enter fullscreen mode Exit fullscreen mode

Subscribing to an Observable

myObservable.subscribe({
  next(value) {
    console.log(value);
  },
  error(err) {
    console.error('Error:', err);
  },
  complete() {
    console.log('Completed');
  }
});
Enter fullscreen mode Exit fullscreen mode

Output

First value
Second value
Completed
Enter fullscreen mode Exit fullscreen mode

Key Differences Between Promises and Observables

  • Single vs. Multiple Values: Promises resolve to a single value, while Observables can emit multiple values over time.
  • Eager vs. Lazy: Promises are eager, meaning they start executing immediately, whereas Observables are lazy and do not start until they are subscribed to.
  • Cancellation: Promises cannot be canceled once initiated. Observables can be canceled by unsubscribing.
  • Operators: Observables come with a rich set of operators (e.g., map, filter, debounce) for transforming and composing data streams.

Examples

Example 1: Simple Asynchronous Operation

Using Promises

const fetchDataPromise = () => {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      resolve('Data fetched using Promise');
    }, 1000);
  });
};

fetchDataPromise().then(data => {
  console.log(data);
});
Enter fullscreen mode Exit fullscreen mode

Output

Data fetched using Promise
Enter fullscreen mode Exit fullscreen mode

Using Observables

import { Observable } from 'rxjs';

const fetchDataObservable = new Observable(observer => {
  setTimeout(() => {
    observer.next('Data fetched using Observable');
    observer.complete();
  }, 1000);
});

fetchDataObservable.subscribe({
  next(data) {
    console.log(data);
  },
  complete() {
    console.log('Completed');
  }
});
Enter fullscreen mode Exit fullscreen mode

Output

Data fetched using Observable
Completed
Enter fullscreen mode Exit fullscreen mode

Example 2: Handling Multiple Values Over Time

Using Promises

Handling multiple values over time with Promises involves chaining multiple Promises, which can be cumbersome.

const fetchFirst = () => {
  return new Promise(resolve => {
    setTimeout(() => {
      resolve('First value');
    }, 1000);
  });
};

const fetchSecond = () => {
  return new Promise(resolve => {
    setTimeout(() => {
      resolve('Second value');
    }, 2000);
  });
};

fetchFirst().then(value1 => {
  console.log(value1);
  return fetchSecond();
}).then(value2 => {
  console.log(value2);
});
Enter fullscreen mode Exit fullscreen mode

Output

First value
Second value
Enter fullscreen mode Exit fullscreen mode

Using Observables

Observables handle multiple values naturally.

import { Observable } from 'rxjs';

const multiValueObservable = new Observable(observer => {
  setTimeout(() => {
    observer.next('First value');
  }, 1000);

  setTimeout(() => {
    observer.next('Second value');
  }, 2000);

  setTimeout(() => {
    observer.complete();
  }, 3000);
});

multiValueObservable.subscribe({
  next(value) {
    console.log(value);
  },
  complete() {
    console.log('Completed');
  }
});
Enter fullscreen mode Exit fullscreen mode

Output

First value
Second value
Completed
Enter fullscreen mode Exit fullscreen mode

Example 3: Cancellation

Using Promises

Promises cannot be canceled once they are initiated.

const nonCancellablePromise = new Promise((resolve, reject) => {
  setTimeout(() => {
    resolve('This cannot be canceled');
  }, 3000);
});

// You cannot cancel this promise once started.
Enter fullscreen mode Exit fullscreen mode

Using Observables

Observables can be canceled by unsubscribing.

import { Observable, Subscription } from 'rxjs';

const cancellableObservable = new Observable(observer => {
  const timeoutId = setTimeout(() => {
    observer.next('This will not be logged');
    observer.complete();
  }, 3000);

  // Cleanup logic
  return () => {
    clearTimeout(timeoutId);
    console.log('Observable canceled');
  };
});

const subscription: Subscription = cancellableObservable.subscribe({
  next(value) {
    console.log(value);
  },
  complete() {
    console.log('Completed');
  }
});

// Cancel the Observable after 1 second
setTimeout(() => {
  subscription.unsubscribe();
}, 1000);
Enter fullscreen mode Exit fullscreen mode

Output

Observable canceled
Enter fullscreen mode Exit fullscreen mode

When to Use Promises vs. Observables

  • Use Promises:

    • When you need to handle a single asynchronous operation that resolves once (e.g., HTTP requests, reading a file).
    • When you want simplicity and don't need the advanced capabilities of Observables.
  • Use Observables:

    • When dealing with multiple values over time (e.g., user input, WebSocket connections, real-time data).
    • When you need to cancel the asynchronous operation.
    • When you need advanced operators for data transformation and composition.

Conclusion

Both Promises and Observables are powerful tools for managing asynchronous operations in JavaScript. Promises are simpler and great for single operations, while Observables offer more flexibility and are ideal for complex scenarios involving multiple values and real-time data streams. Understanding the strengths and use cases of each will help you make informed decisions in your development projects.

By mastering Promises and Observables, you can write more efficient, readable, and maintainable asynchronous code, improving the overall performance and user experience of your applications.

Top comments (0)