Reputation: 21030
As far as I understand, following are the techniques to solve asynchronous programming workflows:
Newer approaches:
We are now moving away from callbacks & promises to these newer approaches. What I understand currently is - Async/Await is more like a cleaner abstraction on top of ES2015 generators.
What I am not able to understand is the conceptual difference between Observables and Generators. I have used both extensively and have no trouble in using them.
What confuses me is the use case for Observables and Generators. I have come to conclusion that, in the end, they are addressing the same problem - asynchronicity. Only potential difference I see is generators inherently provide imperative semantics to code while Observables using Rxjs seem to provide reactive paradigm. But is that it?
Should that be the criteria to choose between Observable and Generator? What are the pros and cons.
Am I missing the big picture?
Also with Observable eventually making into future Ecmascript, are Promises (with cancelable-token)/Observable/Generators going to compete with each other?
Upvotes: 50
Views: 8426
Reputation: 4300
I understand the question was posted in 2016 where generator
were still used synchronously.
However, I think a more interesting (suitable) question today is the difference between async generator
and reactive observable
or reactive programming
in general. Please allow me to digress.
In the following, I'll use:
A
to represent async/await, promise
etc.B
to represent async generator
C
to represent reactive observable
.First of all, all of them are designed to deal to async
data. It's the detail that matters.
If we have a finite and small async
data source, such a single HTTP request, a few KBs of data on disk. Then A
wins, simple and clear.
When the data source became infinite (e.g. user clickings) or too big to hold in memory, then A
won't work well because we will run into accidental complexity very quickly (dealing with backpressure to start with). B
and C
both shines in general, as they both provide backpressure facilities in one way or another.
Then, when the data source starts to requiring buffering, de-duping, debounce, merging/interleaving with other data source etc, C
wins as many library will provide generic operators to help dealing with common stream issue. A example use case, is stop user from spamming comments in short window (e.g. 5s).
Does it mean C
is better than B
in general?
In my opinion, no. Despite C
being more versatile, B
is simpler, more flexible, and integrate with the language a bit better (e.g. for await ... of
). And it does not require you to almost change your entire programing paradigm as per C
would require.
Upvotes: 7
Reputation: 4349
Observables push changes, and hence the observable, not the function reacting to it, is in control. Generators on the other hand require you to pull values out of them. So the function that will react to the new value determines when it is ready for a new value.
I had trouble with backpressure using observables, but with generators, you can let values out as slowly as you want.
Edit: the last question. Promises are just observables that only emit once, so I don't think they will compete with each other. I think the real battle will be async/await vs observables, and async/await has a head start, and is already in C# (and now Node.js). But observables have that sweet FRP feel, and functional programing is super cool, so I think they will both end up with a good chunk of mindshare.
Edit2: André Staltz, author of Cycle.js and xstream, and contributor to Rx.js, wrote a great article on how generators and Observables relate (on 2018-01-31). In particular, he shows how they both inherit from a common base class.
And now a consumer can be a Listener (“observer”) or a Puller, it’s up to the consumer whether it will pull the producer or not. And the producer can be a Listenable (“observable”) or a Pullable (“iterable”), it’s up to the producer whether it sends data proactively or only on demand. As you can see, both consumer and producer are simple functions of the same type:
(num, payload) => void
So any operator that we build will work for both reactive programming or iterable programming, simply because the line between those two modes gets blurred and it’s not anymore about observables versus iterables, it’s just about transformations of data between producer and consumer.
I recommend reading it [link]. The article introduces "Callbags", a spec for callbacks used for reactive and iterable programming. He implements that spec to make a tiny library for both iterable and reactive programming. To entice you to read the article and check out the library, here are some examples from the 7kb lib based on the spec he introduces:
Reactive programming example
Pick the first 5 odd numbers from a clock that ticks every second, then start observing them:
const {forEach, interval, map, filter, take, pipe} = require('callbag-basics');
pipe(
interval(1000),
map(x => x + 1),
filter(x => x % 2),
take(5),
forEach(x => console.log(x))
);
// 1
// 3
// 5
// 7
// 9
Iterable programming example
From a range of numbers, pick 5 of them and divide them by 4, then start pulling those one by one:
const {forEach, fromIter, take, map, pipe} = require('callbag-basics');
function* range(from, to) {
let i = from;
while (i <= to) {
yield i;
i++;
}
}
pipe(
fromIter(range(40, 99)), // 40, 41, 42, 43, 44, 45, 46, ...
take(5), // 40, 41, 42, 43, 44
map(x => x / 4), // 10, 10.25, 10.5, 10.75, 11
forEach(x => console.log(x))
);
// 10
// 10.25
// 10.5
// 10.75
// 11
Upvotes: 37
Reputation: 21339
You can consider the rxjs observables as asynchronous generators i.e. generators yielding promises. Just because the content is not guaranteed to be ready at the time we call .next (in opposition to regular generators)
asynchronous Iterators proposal
Upvotes: 4