What are the differences between Promises, Observables, and Streams?
Inspired by a discussion with a colleague of mine who wanted to know if Observable and promise are the same thing, I decided to write this blog to explain the differences between them.
Promises vs. Observable
Observables are like Promises, except they work with multiple values, they clean up after themselves, they can be cancelled. If the result of a HTTP request or some asynchronous operations isn’t needed anymore, the Subscription of an Observable allows to cancel the subscription, while a Promise will eventually call the success or failed callback even when you don’t need the notification or the result it provides anymore.
An Observable is like a Stream (in many languages) and allows to pass 0, 1, or more events where the callback is called for each event. They deal with a sequence of asynchronous events.
Observables provide the features of Promise and more:
- they can have multiple values over time: if we keep the subscription to the newsletter open, we will get a new one every once and a while. The sender decides when we get it but we have to wait until it comes.
- they have multiple pipelines
- they support aggregate operations like map, filter, forEach, reduce, …
- we can do some powerful functions like combining different
Observables to a new one with
Since Observables are used in reactive programming which deals with a “sequence of asynchronous events”, let’s see with this real-life example from Uladzimir Sinkevich what does this mean:
Say, it’s Friday and John wants to spend this evening with his friend Bob, scarfing pizza and watching one of the Star Wars episodes. Let’s outline the options he has.
- John finishes his work. Then goes and orders the pizza, waits till it’s done. Then picks up his friend. And finally (with Bob and pizza) makes it home and gets down to the movie. It will be the sync approach and it will be way too long, so that probably John will have wanted to call the thing off by that time.
- John orders his pizza online, phones Bob, invites him to come. He heads home, has his pizza delivered and starts watching the movie (and eating the pizza) without waiting for Bob to show up. That is what can happen with the async approach.
- John orders pizza, phones Bob, invites him to come, heads home, and gets his pizza delivered. But this time, he waits until Bob comes and only after that he turns the movie on. This is what the reactive approach is about. You wait till all async actions (changes) are completed and then proceed with further actions.
“Reactive programming is programming with asynchronous data streams.” Andre Staltz
Observable vs. Streams
At this stage and after seeing what we can do with an Observable, my colleague asked the next good question:
“Can we handle an Observable (in frontend) the same way we handle streams in Java since they have similar operators?”
Observable and Streams look pretty similar. They have look alike operators (filter, map, …) but Observable differs from Streams significantly:
- A Stream is simply a Collection that arrives over time.
- Observables are like collections… except they arrive over time asynchronously.
- Stream can only be used once, Observable can be subscribed to many times.
- Stream is pull-based: the data-consumer decides when it gets data from the data-producer. The producer is unaware of when data will be delivered to the consumer. This only works with synchronous things. To pull a value from a collection, it has to be available now! If we think about synchronous as “pull”… we can think about asynchronous as “push”…
An Observable is a producer of multiple values, pushing them to subscribers. We subscribe to an Observable and we will get notified when the next item arrives
onNext, or when the stream is completed
onCompleted, or when an error occurred
onError. Because with
Observable we receive
onError events. Other stuff we could do is caching, throttling, ...
Java 8 Streams API vs RxJava
Let’s take the example of Streams from Java 8 Streams API (java.util.stream) and Observables from RxJava (the ReactiveX API for Java, for asynchronous programming with observable streams). Those 2 APIs are not built for the same usage.
- We can perform asynchonus tasks using RxJava.
- With Java 8 stream, we will traverse items of your collection.
- We can do pretty much the same thing in RxJava (traverse items of a collection) but, as RxJava is focussed on concurrent task, …, it use synchronization, latch, … So the same task using RxJava may be slower than with Java 8 stream.
- RxJava can be compared to
CompletableFuture, but that can be able to compute more than just one value.
- By default RxJava is single threaded. Unless we start using Schedulers, everything will happen on the same thread.
One last thing: Streams vs. Collections
As you’ve noticed, there is a fourth player in our story: it’s the Collection. Java 8 Streams API provides a mechanism to work with Java collections. It’s about converting collections to a stream, processing the elements in parallel and then gathering the resulting elements into a collection.
- A collection is an in-memory data structure which holds elements within it. Each element in the collection is computed before it actually becomes a part of that collection. So, it’s a set of eagerly computed values.
- Streams are fixed data structures which computes the elements on-demand basis. The Java 8 Streams can be seen as lazily constructed Collections, where the values are computed when user demands for it.
- Like functional programming languages, streams support aggregate operations which can be executed in series or in parallel: filter, map, reduce, find, match, sort, limit, collect, …
- Streams also support Pipelining and Internal Iterations: most of java 8 stream operations returns Streams only. This help us creating chain of various stream operations → this is called as pipelining. The pipelined operations looks similar to SQL Query.
That’s it for today!
I hope you enhanced you understanding of promise, observable and stream and that next time you come across those terms they will not bewilder or confuse you.