Promises vs Streams

# Boopathi Rajaa (3 years ago)

I feel this must have already been discussed but couldn't find any discussion threads, just trying to understand them better.

The basic doubt is that I feel promises are more like streams, and that streams are much more powerful than promises. With a promise you have a value or an exception, and with a stream, you have a list of values or an exception.

Why do we have both ? or more specifically, since we have both, when to use Promises and when to use Streams ? Whatever I imagine to be a Promise can be thought out to be solved by Streams, and sometimes whenever I use streams, it feels like I'm using similar API as Promises.

# Anne van Kesteren (3 years ago)

On Sat, Mar 28, 2015 at 1:14 PM, Boopathi Rajaa <legend.raju at gmail.com> wrote:

Why do we have both?

Why do we have both values and arrays, not just the latter?

# joe (3 years ago)

Maybe the confusion stems from how Promises were used in ES5? ES5 doesn't support generators, so people ended up adding a sort of psuedo-generator API to their promise APIs, but in reality the concepts solve different problems? FYI, python seems to use promises and event streams together in its async libraries.

Joe

# Axel Rauschmayer (3 years ago)

Synchronously, we have both normal (synchronous) function calls and iteration over a sequence of values (via for-of and iterators). It makes sense that we also should have two abstractions for asynchronous interaction.

# Domenic Denicola (3 years ago)

The same argument also implies that arrays are more powerful than scalar values, and we should e.g. never use a number when we could instead just use a single-element array with a number.

# Domenic Denicola (3 years ago)

Seeing as how I just produced a completely redundant message by failing to read the other responses before firing off my own, let me try to redeem myself with some more-original content.

It’s also important to realize that streams are not the only asynchronous-plural primitive out there. My favorite analogy is “streams are to asynchronous iterables as arrays are to synchronous iterables.” That is, streams are a specialized data structure, made to efficiently map to lower-level I/O syscalls while still providing a unified abstraction and being an instance of some general async-iteration protocol. Similarly, arrays are a specialized data structure, made to efficiently handle random indexed-access and sequential storage, while still being an instance of the general (synchronous) iteration protocol.

A more general overview of this entire space can be found in Kris Kowal’s General Theory of Reactivity. He explains how there's actually several representatives in each of the four quadrants of sync/async + singular/plural. (Or more generally, "spatial/temporal" + singular/plural.) As you might expect, the most-complicated quadrant (temporal + plural) has the most possible representatives, from async iterators to streams to observables to behaviors to signals.

As for what the general "async iterable" interface might look like, which streams would be a special case of (in the same way arrays are a special case of sync iterables), my favorite candidate at present is zenparsing/async-iteration. It's a straightforward extension of the synchronous iterable interface, and matches with the GTOR's reasoning quite nicely. But, there's no committee consensus on this. You'll also notice that, similar to arrays, the more specialized type is being developed and shipped first (in response to a specific need of the platform), with its generalization following behind.

All of this, of course, doesn't change the fact that it's useful to distinguish between singular and plural data structures. You get many more guarantees with a singular data structure than you do with a plural---similarly to how you get more guarantees working with, say, a value of type number, than you do with a value of type "any". These guarantees can be important for many things, from security invariants to simply being able to reason about the flow of your code. And it's also nice to be able to build one on top of the other, in the way streams are built on top of promises!

# Brendan Eich (3 years ago)

Domenic Denicola wrote:

Seeing as how I just produced a completely redundant message by failing to read the other responses before firing off my own, let me try to redeem myself with some more-original content.

(Nice post!)

It’s also important to realize that streams are not the only asynchronous-plural primitive out there. My favorite analogy is “streams are to asynchronous iterables as arrays are to synchronous iterables.” That is, streams are a specialized data structure, made to efficiently map to lower-level I/O syscalls while still providing a unified abstraction and being an instance of some general async-iteration protocol. Similarly, arrays are a specialized data structure, made to efficiently handle random indexed-access and sequential storage, while still being an instance of the general (synchronous) iteration protocol.

A more general overview of this entire space can be found in Kris Kowal’s [General Theory of Reactivity][gtor]. He explains how there's actually several representatives in each of the four quadrants of sync/async + singular/plural. (Or more generally, "spatial/temporal" + singular/plural.) As you might expect, the most-complicated quadrant (temporal + plural) has the most possible representatives, from async iterators to streams to observables to behaviors to signals.

As for what the general "async iterable" interface might look like, which streams would be a special case of (in the same way arrays are a special case of sync iterables), my favorite candidate at present is zenparsing/async-iteration. It's a straightforward extension of the synchronous iterable interface, and matches with the GTOR's reasoning quite nicely. But, there's no committee consensus on this.

Just noting we haven't pushed for consensus either, we're still doing the hermenuetic spiral. I like Kevin Smith's proposal too, FWIW.