Promises vs Streams
d at domenic.me
Sat Mar 28 17:01:00 UTC 2015
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][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 https://github.com/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!
More information about the es-discuss