DOM EventStreams (take two on Streams): Request for feedback

Domenic Denicola domenic at domenicdenicola.com
Thu Apr 18 19:20:48 PDT 2013


Makes sense, and thanks for clarifying!

I guess my only hesitation is that promises evolved over many years, with the design we see in Promises/A+ today and its many implementations, including DOM Futures, being the result of convergent evolution in library-space. While your sugar is nice, I'd be hesitant to bless it as the one true async-loops sugar without at least some library-space evolution.

In other words, why not try releasing your event streams as a library, and see what kind of adoption they get? If libraries as diverse as jQuery, WinJS, Ember, Angular, Dojo, and YUI end up all having some form of event stream in roughly that format, you know you're on to a winner :).

It's a different scenario from promises, since they're part of the API contract that needs to be exposed when designing web APIs. It sounds like the fundamental API contract for your case is just a changing property, and the method of consuming that changing property could be done with sugar from any library. (Indeed, this is somewhat like how the fundamental API contract for async DOM APIs is now to return a relatively feature-less DOMFuture, which consumers can use alongside any full-featured promise library in order to get the sugar they desire.)

From another perspective, it would be somewhat of a shame to quash the nascent FRP-in-JS industry in its tracks by handing down an API from on high in the WHATWG. Already we're seeing very diverse implementations, from RxJS to Bacon.js to the various Node.js experiments (which range from "core" object-mode streams to the user-land experiments that seem to have sprung up in the last few months, like Dominic Tarr's pull-stream). It reminds me of the .NET space, where Microsoft's entry into an area—whether it be ORMs with Entity Framework, or package managers with NuGet, or build tools with MSBuild—immediately grabbed the majority of developer mindshare, despite arguably-better options being in development and use.

Finally, on the issue of async loops, I'd argue that it's a tentative analogy. It's at least not nearly as clear how you could do a syntactic transformation like you can with promises, e.g. by introducing coroutines (cf. `await`). In fact, async loops to me seem like they'd be expressed as

```js
while (await asyncCondition()) {
    await asyncAction();
}
```

or, of course, its ES6 counterpart with `yield` and task.js-style wrappers.

> -----Original Message-----
> From: Tab Atkins Jr. [mailto:jackalmage at gmail.com]
> Sent: Thursday, April 18, 2013 21:34
> To: Domenic Denicola
> Cc: es-discuss
> Subject: Re: DOM EventStreams (take two on Streams): Request for
> feedback
> 
> On Thu, Apr 18, 2013 at 5:11 PM, Domenic Denicola
> <domenic at domenicdenicola.com> wrote:
> > One bigger question: what is the DOM use case for event streams?
> >
> > That is, it's very clear what the DOM use cases are for binary data
> > streams. (Most urgently, streaming XHR, but also perhaps unifying the
> > many interfaces that use object URLs as a means of connecting separate
> > streams of data; also exposing the browser's GZIP capabilities; and so
> > on [1].)
> >
> > But for event streams it's less clear what urgent problem they solve. The
> example you've shown so far is basically just a different way of doing
> Object.observe, with some nice sugar and of course those combinators. But
> the basic capabilities of the platform are not expanded, and sugar seems like
> a library-level concern. Nevertheless, there's many allusions to DOM use
> cases in your blog posts, so a listing of those would be helpful.
> >
> > In other words: if there are many use cases for the DOM where event
> streams make sense, great! In the spirit of standardizing promises, it's good
> to standardize a common idiom so we don't do things in many different ways
> across the DOM APIs. But if the only use case is just to notify of property
> changes, Object.observe handles that nicely without streams. What else
> needs event streams?
> 
> Almost every use-case for streams *could* be done by just exposing the
> data as a property on some object and using Object.observe.  That loses all
> the really wonderful control-flow properties of streams, though.  One simple
> but very nice example is explained in my blog while illustrating the switch()
> method.  I'll reproduce it here.
> 
> Say you want to provide autocomplete suggestions as the user types into
> some field, based on data on your server.  Using today's technologies, this is
> absolutely possible:
> 
> 1. Register an "input" listener on the input.
> 2. Possibly throttle the input events to keep them from coming in too quickly.
> 3. As the input events come in, construct an XHR to retrieve suggestions from
> your server.
> 4. As each XHR finishes, verify that the results aren't already obsolete by a
> later XHR returning faster.
> 5. If they're not obsolete, update your UI with the returned suggestions.
> 
> Actually writing the code to do all this is surprisingly non-trivial.
> With EventStream and Future, though, it becomes trivial:
> 
> EventStream.listen(input, "input")
>   .throttle(100)
>   .map(e=>getJSON('http://example.com', e.target.value))
>   .switch()
>   .then(updateUI);
> 
> (Assuming that XHR defines a function named getJSON() which returns a
> Future.)
> 
> That's literally all the code you need.  You can even trivially handle and
> recover from XHR errors by adding a second callback to the map() call.
> 
> Even for the "basic" use-cases like I've pointed to in my Font Load API
> proposal, event streams make code *way* easier to write.  Try writing code
> that just updates the UI to tell whether there are currently any fonts loading,
> based on Object.observe().  It's less trivial than one would think:
> 
> Object.observe(document.fonts, changes=> {
>   if( changes.filter(change=> change.type==="name" &&
> change.name==="loadStatus").length )
>     updateLoadingUI(document.fonts.loadStatus);
> });
> 
> Most of this is completely boilerplate, and occupied solely with "fixing" the
> data from Object.observe into a better form.  On the other hand, with a
> ValueStream:
> 
> ValueStream.watch(document.fonts,
> 'loadStatus').squash().then(updateLoadingUI);
> 
> (And I'm considering folding "squash" into the default behavior of
> ValueStreams, too, so it would be even simpler.)
> 
> Yes, a lot of the weight of Object.observe could be lessened by syntax sugar.
> At least part of the point of ValueStream is to *be* that syntax sugar.
> 
> Ultimately, though, I'll just point you back to your own blog post at
> <http://domenic.me/2012/10/14/youre-missing-the-point-of-promises/>.
> ^_^  Just like how Futures capture the notion of async control flow and
> errors, Stream capture the same for *loops*.
> 
> ~TJ



More information about the es-discuss mailing list