
Make No Promises - _halgari
http://swannodette.github.io/2013/08/23/make-no-promises/
======
graue
This is definitely some impressive performance. In Firefox 24 the core.async
example is significantly faster than When.js on average (though in repeated
trials, the running times vary over wide (and overlapping) ranges).

Some commenters seem to assume David's point is "use core.async because it's
faster than promises". I don't think so. My takeaway from this post is that,
having been persuaded core.async is awesome and will give me joy as a
programmer (and see David's other posts for evidence of that, e.g. [1]),
performance concerns should not prevent me from going for it, because it's
competitive.

For me personally, Clojure and ClojureScript still have a long way to go to
catch up with JavaScript-based development using Node.js and say, Angular. A
smaller community, fewer libraries and less mature tools makes it kind of a
bummer from a social perspective. I'm also not impressed with the
Clojure/ClojureScript code-sharing story, which requires hacks[2] to use the
same code on the client and server. Here, Node frameworks like Derby[3], which
emphasizes sharing nearly all your code, and Airbnb's Rendr[4], seem light-
years ahead.

But when you consider that this is an early release of a language and async
lib written by a surprisingly small team, with basically no corporate backing,
it's a pretty amazing accomplishment. I love ClojureScript in theory and I'm
eager for the day when I can love it in practice too.

[1] [http://swannodette.github.io/2013/07/31/extracting-
processes...](http://swannodette.github.io/2013/07/31/extracting-processes/)

[2] [https://github.com/lynaghk/cljx](https://github.com/lynaghk/cljx)

[3] [http://derbyjs.com/](http://derbyjs.com/)

[4] [https://github.com/airbnb/rendr](https://github.com/airbnb/rendr)

~~~
ddellacosta
_I 'm also not impressed with the Clojure/ClojureScript code-sharing story,
which requires hacks[2] to use the same code on the client and server._

I think this is definitely a "the glass is half-empty" interpretation. I mean,
let's point out that you're complaining about an environment where you can
share code between a codebase running on the server and one on the client--and
it isn't JavaScript. And it's not ideal? Of course it's not ideal. But you can
do inter-op with Java (server-side) and JavaScript (client-), using the same
syntax, and with cljx, you can do this in the same _file_. This is freaking
awesome, even with the messy bits that come along with it, in my opinion.

 _A smaller community, fewer libraries and less mature tools makes it kind of
a bummer from a social perspective._

As far as this point, there's less I can disagree with: the community, while
small, is awesome, but there is a way to go before ClojureScript, at least, is
going to have the ease of "install and get going" that you have with other
platforms. Everything from testing to getting a reasonable console up and
running is in a state of flux and cannot be favorably compared to JavaScript,
other than the argument that ClojureScript provides a better enough
programming experience to outweigh the ease-of-setup that JS provides. For me
that's more than enough, but for others I can understand that it's not, yet.

But it absolutely is improving, very quickly. We really need more people to be
using CLJS and hyping it (as David Nolen is doing) and putting in the time to
shave the yaks that need shaving--as people like David Nolen, Kevin Lynagh
([http://keminglabs.com/blog/cljs-app-
designs/](http://keminglabs.com/blog/cljs-app-designs/) as well as the
aforementioned cljx), Chas Emerick (many contributions, but recently
[https://github.com/cemerick/austin](https://github.com/cemerick/austin)),
Mimmo Cosenza (the fantastic Modern CLJS series:
[https://github.com/magomimmo/modern-
cljs](https://github.com/magomimmo/modern-cljs)) and many others are doing--so
that we can get to that point where the advantages clearly outweigh the
disadvantages. I think that time will come, but it's going to require some
serious work.

But even now there are compelling reasons to use ClojureScript, including libs
like core.async. I think you'd find that, with a bit of patience, there is a
lot there to keep you invested in the tools and the community.

EDIT: I just want to add that cljx is not the only or oldest or simplest way
to share code. Crossovers using lein-cljsbuild have provided this for a while:
[https://github.com/emezeske/lein-
cljsbuild/blob/master/doc/C...](https://github.com/emezeske/lein-
cljsbuild/blob/master/doc/CROSSOVERS.md)

~~~
graue
Thanks; great response. I had forgotten about crossovers.

I did a little project in CLJS and found the additional power of the language
nice, but not revolutionary. I've found getting into Node.js that despite the
well-known warts of JavaScript, the community has done an amazing job working
around those problems, building powerful abstractions, and shipping simple,
un-complected code. I hardly feel like I'm compromising on expressivity.

So for me, language like "callback hell" and "wasteful" rings a bit hollow, as
it doesn't reflect my experience writing JavaScript. In fact, if I were a
contributor to When.js I could imagine it being pretty insulting.

Maybe we can win more people to ClojureScript by recognizing, not diminishing,
the great stuff folks have made with vanilla JS, and showing how CLJS provides
interop while making JavaScript programming even better. Kevin Lynagh's post
here[1] is a nice example of that.

[1] [http://keminglabs.com/blog/angular-cljs-weather-
app/](http://keminglabs.com/blog/angular-cljs-weather-app/)

As for my "glass is half-empty" interpretation of code-sharing, I'm thinking
of how you can trivially require jQuery from a Node.js application and use it
for scraping[2]. Or how you can require('http') in the browser and
effortlessly make HTTP requests in browser or server with the same code[3].
I'd love to be proven wrong, but ClojureScript seems far away from this kind
of stuff. You can't use Compojure or HTTP-Kit in ClojureScript. So you could
say that while Clojure has a _decent_ client/server code-sharing story,
there's this other thing, JavaScript, that has an _amazing_ one.

[2] [http://blog.nodejitsu.com/jsdom-jquery-in-5-lines-on-
nodejs](http://blog.nodejitsu.com/jsdom-jquery-in-5-lines-on-nodejs)

[3] [https://github.com/substack/http-
browserify](https://github.com/substack/http-browserify)

------
tantalor
I think this article is comparing apples to oranges.

The promise pattern is not meant to speed up callback-based code, or to
eliminate callbacks altogether.

The purpose of a promise is to allow an asynchronous operation to be
cancelled. In the old way, there'd be no standard way to cancel something like
this,

    
    
      xhr.get(url, function (data) { 
        // do something with the data
      });
    

If we have a promise, we can cancel it,

    
    
      getPromise = xhr.get(...);
      
      // later...
      getPromise.cancel();
    

By creating a standard promise pattern, you can have any number of
heterogeneous asynchronous operations chained or canceled together uniformly.

~~~
mag487
That's a really good point. I hadn't been completely sold on the value of
promises over callbacks before, but this seems like a very unambiguous
advantage. I can't think of an obvious, clean way to do this with callbacks
that doesn't go a long way toward simply re-implementing promises.

I imagine an advocate of core.async would argue that they have a good analog
for canceled promises, though, namely closing channels. On the other hand,
closed channels will still yield nil when read from; the procedures trying to
read from a closed channel would need to check for that.

~~~
jonahx
> I can't think of an obvious, clean way to do this with callbacks that
> doesn't go a long way toward simply re-implementing promises.

I may be misunderstanding your point, but you could name the callback
function, and give the function object itself a "cancel()" method which alters
its behavior to do nothing.

~~~
mag487
Yes, but that doesn't give you a great way to deal with attaching and
canceling multiple callbacks simultaneously. If you want to perform several
independent asynchronous actions after some other operation finishes, you have
to collect them into a single giant callback in one location. And as you
mention, the function has to keep track of some piece of mutable state in
order to decide whether it's cancelled or not before firing off those
operations. So why not give the function a mutable array of asynchronous
operations to perform to begin with, which you can then add to at any time?
And at that point, you already have a something very close to a promise.

------
abecedarius
As graue and David wrote, the main point is not to win on this microbenchmark,
but that core.async is competitive. Since a bare-bones version of promises is
easy to code, though, I tried it out for myself:

[https://gist.github.com/darius/6326461](https://gist.github.com/darius/6326461)

In Firefox Nightly it's usually slower than both of the OP's examples, while
in Chrome it's substantially faster than both. So I'm curious how when.js goes
faster on Firefox while supporting more features, and whether it could be
tuned to catch up with my naive code on Chrome.

------
kevingadd
JS promise libraries are slow because they're poorly factored and constrained
by the limitations of the JS runtime environment and the low quality of
typical JS code. Don't extrapolate from the miserable experience of using
promises in JS to assume that promises are useless as a programming construct.

core.async looks interesting though; I assume I can't just consume it as a
library and instead must rewrite my whole application in (clojurescript? you
literally never mention in this post what language that is)? Too bad, but I
can see how for this type of thing you need compiler support.

~~~
Rayne
core.async is a Clojure library and is built with no compiler support.

~~~
eggsby
To be fair, a lot of the syntax magic of core.async happens in its macros --
in js that is supported by the cljs compiler. As I understand it, because of
this it'd be impossible for core.async to be used as a js "library" (a global
namespace with a public interface).

------
esailija
In terms of performance there is huge room of improvement in when.js.

    
    
        $ node --trace_inlining go_when.js
        Did not inline fulfilled called from coerce (target contains unsupported syntax [early]).
        Did not inline NearFulfilledProxy called from fulfilled (target requires context change).
        Did not inline near called from fulfilled (target contains unsupported syntax [early]).
        Did not inline Promise called from near (target requires context change).
        Did not inline enqueue called from scheduleConsumers (target requires context change).
        Did not inline b called from enqueue (target not inlineable).
        Did not inline fn called from NearFulfilledProxy.when (target requires context change).
    

Also some functions are constantly being recompiled and try catch is
corrupting (making them not optimizable under current v8) some large functions
instead of being isolated.

------
curiousdannii
Author's shouldn't assume that every reader can identify every language by
sight! Thanks HN commenters for identifying core.async as a Clojure library.

------
stormbrew
I find this comparison weird. To me this _is_ effectively a promise library in
the general sense. That promises in js require you to take the promised value
in a callback is a limitation of the js environment, not the concept of
promises themselves. And it's a limitation I assume core.async is no more able
to escape at its lowest levels than When.js is.

I grant that go/core.async-style channels are a superset of promise/future
kind of functionality, but to me they are definitely part of the same family.
And you can definitely implement promises in terms of them.

~~~
eggsby
To paraphase Rich -- "promises are the one-night stand of asynchronous
programming, core.async is aiming for a longer term relationship."

The big win isn't that you have the ability to await and fulfill messages, but
rather that you have first class entities that can be used to send and receive
discrete and coordinated messages.

What's better than simply having these communication channels (think event
emitters) is having the ability through clear syntax to synchronize not only
the receipt of these messages, but also to coordinate message sending through
the same uniform style.

To me, this seems just about as related to promises as promises are related to
callbacks, in that they are all patterns for asynchronous programming.

To drive the point home: "I grant that promises are a superset of callbacks
kind of functionality, but to me they are definitely part of the same family.
And you can definitely implement promises in terms of them."

~~~
stormbrew
I don't think that it's true that promises are a superset of callbacks.
Although, again, owing to the particular nature of javascript the
implementations may be. A promise is like a go channel that can only be sent
to once. A callback has utterly arbitrary behaviour: it can be called many
times with many values in many contexts.

Promises and channels are synchronization primitives. Callbacks are not.

------
noelwelsh
> Promises are all the rage in the JavaScript world even though they don't
> actually eliminate callback hell and their design emphasizes coarse grained
> asychronous operations leaving much to be desired for the vast universe of
> possible interactive applications (cough, user interfaces).

Bold (and interesting) claims. I await the blog post substantiating these
claims.

~~~
abecedarius
One way to test the claim: use promises to code his example
[http://swannodette.github.io/2013/08/17/comparative/](http://swannodette.github.io/2013/08/17/comparative/)
. Is it necessarily hellish? I might get around to looking into this tomorrow
if nobody else does.

------
acjohnson55
That's nice and all, but can I improve upon promises without using another
language on top of JS? I don't have any issue with such languages in
principle, but I spend most of my time working in plain JS codebases these
days, and I'm wondering if I can improve upon promise-based asynchronicity.

------
andrewvc
Fascinating stuff, however, as a clojure user, I just don't care yet. JS UI
frameworks are made of dev ease of use, not speed. I'm glad this stuff exists,
I just hope that useable frameworks are the next phase in the development of
clojurescript.

~~~
benatkin
Well, the start of it still applies. Promises aren't better than callbacks.
Some people prefer them, but they aren't better.

------
chenglou
Does anyone know to what extend the new yield operator replaces the need for
promises? Can it be used for most of promises' purposes or do the latters have
their particularly nice use cases? It's still confusing after a while.

~~~
blueveek
Promises and generators actually work magnificently well together. I would
recomend taking a look at an implementation of Task.js [1] [2] and, for
example, some of its (increasing) usage across the Mozilla codebase [3].

[1] [https://github.com/mozilla/mozilla-
central/blob/master/toolk...](https://github.com/mozilla/mozilla-
central/blob/master/toolkit/modules/Task.jsm)

[2] [https://developer.mozilla.org/en-
US/docs/Mozilla/JavaScript_...](https://developer.mozilla.org/en-
US/docs/Mozilla/JavaScript_code_modules/Task.jsm)

[3] [http://dxr.mozilla.org/mozilla-
central/search?tree=mozilla-c...](http://dxr.mozilla.org/mozilla-
central/search?tree=mozilla-central&q=Task.spawn&redirect=true)

------
blueveek
These comparisons bring up memories of when we used to benchmark empty for
loops. I think today’s microbenchmarks are starting to feel just like that.

Comparing the execution time of a _hundred thousand_ (sync or not) callbacks
vs. a _hundred thousand_ (sync or not) promises is, in many cases, not a good
or deciding factor for what tool you should pick from the toolbox. I would
assert that in realistic scenarios, it really doesn't matter.

~~~
cgag
To be clear, this is benchmarking a go-like channel abstraction, which is
nicer than callbacks or promises imo, which is why it's nice to see it's
performant.

------
sankha93
Strangely I find the promises benchmark faster than the core.async one on the
Firefox OS browser.

~~~
AndreasFrom
The author's point is that core.async is competitive in speed, not necessarily
faster, but better abstractions than promises.

------
Void_
What a confusing title.

------
Hayvok
Guess what - in the time that it took me to read this post, Moore's Law solved
the problem.

Next.

~~~
mag487
How's that? JS is single-threaded.

~~~
jbigelow76
Wouldn't that just mean the event loop cycles faster, lending credence to the
gp's snark?

~~~
mag487
Why would having multiple cores make the event loop cycle faster? Maybe I
misunderstood and he was using "Moore's law" to refer to exponential increase
in processor speed rather than transistor density (which is what I understand
Moore's law to be talking about). But commercial processors aren't really
getting much faster anymore.

