Promise Vs Observable
Let's break down the key differences between Observables (from RxJS) and Promises (native JavaScript). While both deal with asynchronous operations, they have fundamental differences in how they work and what they're capable of.
Here's a comparison table and explanations:
Feature | Promise | Observable |
---|---|---|
Values Emitted | One value (or one rejection) | Multiple values over time (or error/complete) |
Execution | Eager: Starts immediately upon creation | Lazy: Starts only when subscribed to |
Cancellable? | No (standard Promises aren't cancellable) | Yes (via Unsubscription) |
Operators | Limited (.then() , .catch() , .finally() ) |
Rich set of operators (map, filter, retry, etc.) |
Use Cases | Single async events (HTTP requests, timers) | Streams of events, complex async flows, state |
Primary Library | Native JavaScript ES6+ | RxJS library (often used with Angular) |
Explanation of Differences#
-
Single vs. Multiple Values:
- Promise: Designed to handle a single asynchronous event that will eventually succeed (resolve with a value) or fail (reject with an error). Once a Promise settles (resolves or rejects), it's done. It will never emit another value.
- Analogy: Ordering a specific package online. You wait, and eventually, you get that one package (resolve) or a notification it couldn't be delivered (reject). The transaction is then over.
- Observable: Represents a stream or sequence of values arriving over time. It can emit zero, one, or multiple values. It can also signal an error or completion.
- Analogy: Subscribing to a newsletter or a YouTube channel. You might receive multiple emails/videos over days or weeks (multiple
next
emissions). You could also get an error notification, or the channel might eventually stop publishing (complete).
-
Eager vs. Lazy Execution:
- Promise: A Promise starts executing its asynchronous operation the moment it is created. Calling
.then()
doesn't trigger the operation; it just registers what to do when the already-running operation finishes. - Example:
const myPromise = new Promise(/* executor starts now */);
- Observable: An Observable is lazy. The code inside it (e.g., the function making an HTTP call) doesn't run until someone actually subscribes to it using
.subscribe()
. Each subscription typically triggers a new, independent execution. (Operators likeshareReplay
modify this to share executions). - Example:
const myObservable = new Observable(/* code here runs only on subscribe */); myObservable.subscribe(); // Execution starts now.
- Promise: A Promise starts executing its asynchronous operation the moment it is created. Calling
-
Cancellable:
- Promise: Standard Promises don't have a built-in
.cancel()
method. Once you create a Promise and its operation starts, there's no standard way to tell it, "Stop what you're doing, I don't need the result anymore." (Though browser APIs likeAbortController
can sometimes be used to cancel the underlying operation, like anfetch
request, which then causes the Promise to reject). - Observable: Observables are cancellable via their
Subscription
object. When yousubscribe()
, you get back aSubscription
. Callingsubscription.unsubscribe()
signals to the Observable that the subscriber is no longer interested. This often triggers cleanup logic within the Observable (like clearing intervals or cancelling HTTP requests viatakeUntilDestroyed
or similar mechanisms) and stops further emissions to that subscriber.
- Promise: Standard Promises don't have a built-in
-
Operators:
- Promise: Has basic chaining with
.then()
(for success),.catch()
(for error), and.finally()
(for cleanup). You can also usePromise.all()
,Promise.race()
, etc., for combining promises. - Observable: Comes with the vast RxJS library of operators (
map
,filter
,reduce
,retry
,retryWhen
,debounceTime
,switchMap
,mergeMap
,combineLatest
,withLatestFrom
,shareReplay
,timeout
,find
,delay
,tap
, etc.). These operators allow for powerful manipulation, combination, and control of asynchronous data streams in a declarative way.
- Promise: Has basic chaining with
When to Use Which (General Guidelines)#
Use Promises when:
- You're dealing with a single, one-off asynchronous operation (like a typical HTTP GET or POST request where you expect one response).
- You're working with native browser APIs or libraries that return Promises.
- The complexity doesn't warrant pulling in the full RxJS library.
Use Observables when:
- You need to handle streams of data arriving over time (WebSocket messages, user input events like keystrokes or mouse movements, repeated interval emissions).
- You need the advanced transformation, combination, or control capabilities provided by RxJS operators (retrying, debouncing, throttling, complex filtering/mapping, etc.).
- You need the ability to cancel asynchronous operations cleanly.
- You are working heavily within the Angular framework, which uses Observables extensively (e.g.,
HttpClient
,Router
events,EventEmitter
).
In modern Angular, while you can use Promises, Observables are generally preferred for asynchronous operations, especially when dealing with framework features or requiring more complex stream manipulation, due to their power, flexibility, and cancellable nature.