A
Promise
handles a single event when an async operation completes or fails.
So, a promise doesn't handle anything - a promise is just a value over time. The charactaristics of promises are that:
- Because a promise is just a value - promises are multicast which means multiple
.then
s are 'transparent'. - Native promises are what async functions return and what they
await
. - Promises have three states (fulfilled, pending and rejected) and transition between them just once.
Promises aren't really eager - but if you have a promise an operation has already started - so terms lik running promises don't make sense. They have a built in (stage 4 and part of JavaScript) multi-value counterpart (async iterators) which do multiple values and are lazy.
Note: There are
Promise
libraries out there that support cancellation, but ES6Promise
doesn't so far.
That part is true.
Observable
An
Observable
is like aStream
(in many languages) and allows to pass zero or more events where the callback is called for each event.
So, when I said this in an Angular-beta core data team discussion with Ben Lesh he insisted it's wrong. In retrospect he was right :)
An observable is like a function - when you do new Observable(observer => { })
you are really building a function. I actually show how to build Rx here and explain this. I can't stress this enough but all an observable is is:
constructor Observable {
constructor(subscribe) {
this.subscribe = subscribe;
}
}
Which lets you do:
let o = new Observable(observer => {
let interval = setInterval(() => observer.next('hello'), 100);
return () => clearInterval(interval);
});
// subscription is the clearInterval, this is _just_ a function call
let subscription = o.subscribe({
next(value) { console.log(value, 'world!'); } // this is just the observer argument!
});
That's why Rx is nice, it's just a contract over functions :)
Often
Observable
is preferred overPromise
because it provides the features ofPromise
and more.
That's basically like saying arrays are better than values because arrays have multiple values. I think it's confusing:
- If you want to return a single value you should use a promise.
- If you want to create an eager push stream over multiple values - you should use an observable.
Having a smaller API surface is generally better than a big one if possible. While RxJS is awesome I think this is risky advice.
With
Observable
it doesn't matter if you want to handle 0, 1, or multiple events. You can utilize the same API in each case.
That's also true with async iterators (which are stage 4 and built into and have syntax support) and any other multi-part abstraction. We're currently evaluating how to use different abstractions in Node.js core (If you want to be part of this discussion you're invited!).
Observable
also has the advantage overPromise
to be cancelable.
Well, a subscription is cancellable - an observable is not cancellable since it's a function and not values.
That said - an observable is not a static JavaScript feature, you are comparing apples to oranges. You are basically saying that the RxJS library is cancellable while native promises are not. That's fine - but it's important to say that.
Since an observable is just a function call, if that function (subscribe) returns a function - it can do unsubscriptions if it wants to.
If the result of an HTTP request to a server or some other expensive async operation isn't needed anymore, the
Subscription
of anObservable
allows to cancel the subscription, while aPromise
will eventually call the success or failed callback even when you don't need the notification or the result it provides anymore.
Promise based APIs do cancellation - just not through the Promise itself. For example AbortController
with fetch
is a DOM API that returns a promise but is cancellable.
Observable provides operators like
map
,forEach
,reduce
, ... similar to an array
That's a property of the RxJS library (or other libraries) and not the abstraction. Here is an example where I implement it over Async Iterators - note I wrote this when it was a very early stage proposal so it might be outdated.
Of course promise libraries also provide operators (namely bluebird provides .map
.forEach
and .reduce
).
It is also a little risky to compare observables to arrays. The best explanation of Rx I know is Duality and the End of Reactive but I have to admit I saw it after using Rx for 3-4 years already.
There are also powerful operators like
retry()
, orreplay()
, ... that are often quite handy.
That's again a feature of the library and not the abstraction.