
Better JavaScript Promises - fvdessen
http://frederic.vanderessen.com/better-javascript-promises/
======
tomp
I vehemently oppose this proposal. The syntax of the proposed JavaScript
addition is confusing and misleading, and will break more programs than
JavaScript's unfortunate `this`.

In particular, the following code snipped, copied from the blog post, goes
against everything we've been taught by modern programming languages:

    
    
      var a = future;  
      var b = a;  
      console.log(a,b);   // will print 'hello' 'world'  
      b = 'world';        // this does not resolves 'a'  
      a = 'hello';
    

It breaks the concept of variables being references to objects. This is
similar to Matlab's treatment of matrices, that none of it's successor
languages (Python/NumPy, Julia) emulates.

Furthermore, it's confusing in the way that `a` could mean both (1) get value
referenced by variable `a`, and (2) _wait_ until value referenced by `a` is
available. I believe that a much better proposal would be to treat futures as
a separate data type (`a = future()`), and implement getting and fulfilling
the future as method calls (`a.set(1)` and `a.wait()`).

~~~
IgorPartola
Your example illustrates the problem nicely. The code above is impossible to
parse without knowing that "future" is a special value. Note that after you
assign `a = future` you have no idea that a is special. Separate the code into
three modules: the first contains lines 1 and 2, the second contains line 3
and the third contains lines 4 and 5. Now, looking at the second module you
have no idea if the execution blocks on `console.log(a,b);` or not.

More importantly, even if all the code is written as nicely as the example
above you still cannot reason about this like you could about every other
goddamn language out there. You can't pick arbitrary lines of code from the
listing and execute them in that order, and if you try to figure out how it
works (even if it's written without deadlocks) you will break your brain.

Instead, let's something like $q [1], or even just the smaller/simpler
AngularJS implementation of it [2]. While requiring callbacks, it's still very
workable and does simplify things. Adding chained callbacks is very nice:

    
    
        var resp = $.get('/my/url');
        showLoadingScreen();
    
        resp.then(function(data) {
            return JSON.parse(data);
        }
        
        resp.then(function(data) {
            alert('data.username = ' + data.username);
        }
        
        resp.catch(function(err) {
            alert('Error: ' + err);
        })
        
        resp.finally(function() {
            hideLoadingScreen();
        })
    
    

[1] [https://github.com/kriskowal/q/wiki/API-
Reference](https://github.com/kriskowal/q/wiki/API-Reference)

[2]
[https://docs.angularjs.org/api/ng/service/$q](https://docs.angularjs.org/api/ng/service/$q)

~~~
tomp
The point is, extending JS to support such constructs _without_ requiring
callbacks would be worthwhile, IMO [1]. However, making it obvious with a
better API would be necessary.

[1] Note, however, that it would turn JS's threading model completely on its
head. Right now, the philosophy of JS is "single-process" threading model,
with WebWorkers supporting a "multi-processing" concurrency model by isolating
different threads of control. Allowing futures without callbacks would bring
JS closer to Golang's concurrency model.

------
cwp
Ugh. There are (at least) two fundamental problems here:

First, depsite the ugliness that asynchronous code entails, it does have one
very important feature: it never blocks, which makes reasoning about it much
easier. This proposal would make it possible for Javascript code to block,
which enables a whole class of bugs that were impossible before.

    
    
      var a = future;
      var b = future;
    
      setTimeout(function() {
        a = b+1;
      },0);
    
      setTimeout(function() {
        b = a+1;
      },0);
    
      console.log(a, b);
    

Hello deadlocks!

Second, futures aren't first-class values. The proposal specifies that
assigning a future to a variable doesn't create a second reference to the
future, it creates a child future that would be resolve by the resolution of
the parent future.

Thus, a future must be resolved in the scope in which it's created, or the
resolution won't be propagated to all its children. With promises, you dont'
have to worry about the side effects of assignment or function calls. Take
this common pattern for wrapping an asynchronous call with a promise:

    
    
      function promiseMeSomething() {
        var d = defer();
        doSomethingAsyncronous(function(err, value) {
          d.resolve(value);
        });
        return d.promise;
      }
    

We could do the same with futures:

    
    
      function futureMeSomething() {
        var f = future;
        doSomethingAsyncronous(function(err, value) {
          f = value;
        });
        return f;
      }
    

But that callback function is repetitive, so we'll refactor:

    
    
      function makeCallback(d) {
        return function(err, value) {
          d.resolve(value);
        }
      }
    
      function promiseMeSomething() {
        var d = defer();
        doSomethingAsyncronous(makeCallback(d));
        return d.promise;
      }
    

But consider the same refactoring with futures:

    
    
      function makeCallback(g) {
        return function(err, value) {
          g = value;
        }
      }
    
      function futureMeSomething() {
        var f = future;
        doSomethingAsyncronous(makeCallback(f));
        return f;
      }
    

Whoops. This won't work, because g is a child future of f and resolving it
won't resolve f.

~~~
grrowl
As well, Javascript's single-threaded execution means blocking execution halts
your whole program. IMO, the way to tackle complex asynchronous callback code
is with smarter code structure and frameworks — just as you should avoid shoe-
horning inheritance OOP into JS, you should write async callback code with
readability and flexibility in mind (without complaining you "have to" write
bad code)

------
Arkadir
The main reason why writing concurrent code in JavaScript is not completely
insane (just moderately so) is because of the single-threaded sequential
execution model (also known as cooperative threading).

Asynchronous effects need to remain explicit.

Consider a simple example:

    
    
        function binary(stack,combine) {
          var a = stack.pop();
          var b = stack.pop();
          stack.push(combine(a,b));
        }
    

In today's JavaScript, this code executes as an atomic sequence. It turns
[..,a,b] into [..,combine(a,b)] and there is no way for another piece of code
to step on this function's toes and break this invariant.

What if "combine()" suddenly became asynchronous ? Either because its
operation itself is asynchronous, or the evaluation of "a" or "b" involved an
asynchronous operation. This would cause the call to "binary()" to pause in
the middle of the function, which would give time to another part of your code
to call "binary()" in turn. And now [a,b,c,d] has turned into [a + b, c + d]
instead of [a, b + c + d], and you'll have a jolly good old time debugging
tht.

Make asynchronous context switches implicit and you start needing locking
primitives. Everywhere, including third party libraries that "used to work".
Because every single line of code(except plain assignment) could be a ticking
async-bomb that will pause the current function and let other functions wreak
havoc on your invariants.

Scary.

Callbacks are ugly, and promises are hardly better, but asynchronous
operations NEED to stay explicit. And explicit asynchronous operations are
contagious. That's just what they are.

Just make the syntax less right-pyramidal: C#'s async/await syntax is a good
candidate, and 'await' is a good way to mark a possible context switch.

------
pornel
ES7 has `async function(){}` and `await call();` syntax proposal that adds the
promise/generator trick to the language.

[http://wiki.ecmascript.org/doku.php?id=strawman:async_functi...](http://wiki.ecmascript.org/doku.php?id=strawman:async_functions)

~~~
jmtulloss
This syntax has been borrowed from other languages and is tried and true.

I feel that the author intuited that something like this would be nice, but
wasn't aware of async/await and so proposed something that only kind of
vaguely points in the right direction. I don't think he's wrong that something
like this would be nice, but async/await is a much more robust way of dealing
with this!

------
jeswin
> Unfortunately, using yield based solution will require third party
> libraries, which means increased payload and incompatibilities among
> frameworks.

Check out co
([https://github.com/visionmedia/co/blob/master/index.js](https://github.com/visionmedia/co/blob/master/index.js)).
For all its features it's tiny. Increased payload and incompatibility is a
blanket statement; that could apply to any library or framework.

> And I am also quite disappointed that the solution to one of JavaScripts
> biggest flaws requires hacking a language feature that was designed for an
> entirely different use-case, which will surely bring lots of confusion.

There should be a really high bar for adding keywords to a language. If
something can be solved with features we already have, we needn't add another.
ES6 generators solves the callback problem for now. There are of course other
(perhaps more flexible) solutions, like Scala's futures and for-comprehensions
[1], or F# computation expressions [2]. But generators are simpler and they
work well enough.

[1] [http://docs.scala-
lang.org/overviews/core/futures.html](http://docs.scala-
lang.org/overviews/core/futures.html) [2]
[http://fsharpforfunandprofit.com/series/computation-
expressi...](http://fsharpforfunandprofit.com/series/computation-
expressions.html)

~~~
camus2
ES6 adds function* which is basically a new keyword. This is ugly by the way.
fortunatly on the server I can use node-fibers.on the client,async libs and
promises are good enough,as people ususally dont have to write 10 async
operations on a row.

------
chriswarbo
Reminds me of call/cc, which amuses me since that's the only (major) feature
of Scheme I hadn't yet seen anyone calling for in Javascript (eg. there are
plenty, including me, who want tail-call optimisation; I've seen pre-compilers
implementing hygenic macros, libraries implementing numeric towers, etc.).

~~~
aaronem
I'd like to see TCO in Javascript, but before that I'd like to see some less
headache-inducing method of dealing with lots of asynchrony.

I'm not at all sure the method described in the linked article would work as
well as said article makes it sound, but I'm definitely in favor of seeing
people much smarter than I am argue this one to a finish and then implement
the last proposal standing.

------
arnarbi
I don't like this. Even the author's own example shows how this gets very
confusing even with simple code:

    
    
      var a = future;  
      console.log(a);     // will print 'hello'  
      a = 'Hello'  
      a += ' World!'  
      console.log(a);     // will print 'Hello World!'  
    

Deferreds/promises are good exactly because they explicitly state what they
are through their type, and they behave the "same" whether they are resolved
or not.

This proposal breaks the notion of what a variable is, and it behaves very
differently whether the future has been replaced by a value or not. This will
become confusing very fast and a great source of race conditions.

------
ken47
There is a bug in the author's first example:

var a = future; setTimeout(function(){ a = 'Hello World'; },1000);
console.log(a);

If evaluating 'future' returns control to the scheduler, the setTimeout never
gets called.

On a higher level, these "flow control" libraries do not, despite their name,
control the flow of logic. The underlying asynchrony remains exactly the same.
They just make it easier to avoid an indentation avalanche, or callback
spaghetti. If and only if spaghetti makes it difficult for you to reason about
code will these "flow controls" make it easier to reason about asynchronous
Javascript.

~~~
chriswarbo
As far as I understand it, when the "future" keyword is hit it woudn't return
control to the scheduler; it would introduce a special deferred value. Control
would only return to the scheduler when something attempts to 'force' that
deferred value.

You can think of these deferred values as being a getter and setter pair,
sharing a reference:

    
    
        var future = function() {
            var x;
            return {'getter': function () {
                while (typeof(x) === 'undefined') wait;
                return x;
            },
            'setter': function(val) {
                x = val;
            }};
        };
    

In the article, future is a keyword rather than a function, and the getter and
setter are called implicitly. The idea is that we can pass around references
(the {'getter', 'setter'} pair) however we like without triggering any action;
however, once the getter is called it will enter a loop, polling and 'waiting'
(switching execution back to the scheduler) as long as the setter's not been
called.

The radical part here is the 'wait', which I've made up to illustrate the
point, and is the crux of the whole thing. It's basically a cooperative
multithreading mechanism. This would let an asynchronous API be turned into a
synchronous one, since we just need to wait for the result with a polling loop
like my example above.

Whether that's a good idea or not is debatable. Personally I think it's a bad
idea; shared mutable state is the cause of _all_ concurrency problems (race
conditions, locks, deadlock, livelock, etc.) so it absolutely should _not_ be
default or implicit. The idea that 'concurrent' == 'multithreaded' needs to
die; multithreading is a dangerous anti-feature which uses a little syntax to
hide a whole new language semantics, breaking all intuitions about our code
and throwing safety properties out of the window.

Thankfully Javascript hasn't fallen into this trap yet; eg. WebWorkers uses
message-passing instead.

------
GeneralMayhem
I was thinking the whole way through, "wait, don't generators fix this?" so it
was good to see that acknowledged at the end. I'm confused by this, though:

>Unfortunately, using yield based solution will require third party libraries,
which means increased payload and incompatibilities among frameworks.

Do you mean that translating to yield requires wrapping things that aren't
expecting it? That's true of moving to any non-callback solution. Or do you
mean that you want to see the native Node APIs implemented with generators
rather than callbacks? I hope the solution to that will simply be patience.

~~~
fvdessen
I mean the yield by itself is not a good API, it has to be wrapped in third
party Deferreds libraries to be useful - there's an (excellent) linked article
which explores this approach : [http://jlongster.com/A-Study-on-Solving-
Callbacks-with-JavaS...](http://jlongster.com/A-Study-on-Solving-Callbacks-
with-JavaScript-Generators)

------
thecolorblue
I think what you are looking for is done, to some extent, in angular.js with
their resource library. The future object is returned from the resource and
can be put directly into a template, which then re-renders when the response
comes from the server. The problem I have found is that it does not clean up
your javascript code as much as you would think. For example, error handling
is still complicated.

I have had good luck using javascript promise objects to clean up complicated
async calls.

for example, making multiple calls and waiting for all responses looks
something like this:

promise = YourFavoriteAsyncLib.all([new Promise('/restaurants'), new
Promise('/shops')]) promise.then(function(res) { // res[0] === restaurants //
res[1] === shops });

...or we can wait for one to finish and then make a couple more calls...

promise = new Promise('/restaurant/1');

promise.then(function(r) { return YourFavoriteLibAgain.all([ new
Promise('/restaurant/' \+ r.id + '/patrons'), new Promise('/restaurant/' \+
r.id + '/menu') ]); }) .then(function(res) { // do more fun things
synchronously });

The details change, and some libraries act a little differently, but once you
break they async calls into steps it becomes easy to read.

I think the point of all of these responses is that async cannot look like
regular statements because they do not act like regular statements.

------
lomnakkus
Genereally speaking async is one of those things that the JS community hasn't
quite figured out yet. (Nor has anyone else, really.)

Personally, what I'd really like to see is an M:1 thread model, i.e. a model
where you program as if you have M threads of control, but only one of them is
actually executing at any one time. Any calls which are currently async would
behave as if blocking and would yield control to any other runnable "thread".
So you would have concurrency but _not_ parallelism -- which is effectively
what you have to reason about anyway when you have async.

It may very well just be me, but I find that reasoning about normal "blocking"
flow control is vastly easier in general than reasoning about async flow
control even if it involves multiple threads. It gets even easier if you limit
all inter-thread communication to some form of queueing/message passing.

EDIT: Reading it back this looks overly convoluted, basically what I'd like in
JS is "user-space threads".

------
michaelsbradley
I would much prefer to see native provision for "channels" and "channelized
blocks", _à la_ the core.async library for Clojure/Script:

[https://github.com/clojure/core.async/blob/master/examples/w...](https://github.com/clojure/core.async/blob/master/examples/walkthrough.clj)

------
jclem
This is tangential to the discussion, here, but if you want to make 3
simultaneous async requests, it's super easy with promises. Here's an example
using bluebird
([https://github.com/petkaantonov/bluebird](https://github.com/petkaantonov/bluebird)):

    
    
      Promise.all([async1, async2, async3]).then(function(results) {
        console.log('async1:', results[0]);
        console.log('async2:', results[1]);
        console.log('async3:', results[2]);
      });
    

or:

    
    
      Promise.all([async1, async2, async3]).spread(function(res1, res2, res3) {
        console.log('async1:', res1);
        console.log('async2:', res2);
        console.log('async3:', res3);
      });
    

With the co library
([https://github.com/visionmedia/co](https://github.com/visionmedia/co)), it'd
look like this:

    
    
      co(function*() {
        var results = yield [async1, async2, async3];
        console.log('async1:', results[0]);
        console.log('async2:', results[1]);
        console.log('async3:', results[2]); 
      })();
    

Until ES6 is available everywhere, the existing promises libraries are so
mature and easy-to-use that I don't really see a problem with them. Bluebird
will even promisify an entire module for you with `promisifyAll`, so mixing it
with callback-based modules is typically trivial.

I think that even though it relies on the use of third-party libraries like
co, the generators solution is much easier to reason about than the proposed
solution here.

I can't find the source, unfortunately, but I remember reading somewhere that
generators were actually designed with this use case in mind, so the claim
that using them for async "requires hacking a language feature that was
designed for an entirely different use-case" is false, if I'm remembering
correctly.

~~~
vkjv
PromisifyAll is awesome... until you realize some code you are using is doing
some seriously crazy stuff.

For example, Mongoose takes all of methods on the prototype during object
creation and replaces them with ones that run middleware. This means that if
you try to use one of the promisified methods, none of the middleware gets
called.

~~~
esailija
I made a fix for a mongoose issue that sounds like what you describe and
landed it in 1.2.3[1]. Can you verify? And if what you describe is a different
issue can you make a github issue? (or is mongoose doing something so crazy
that it cannot be helped at all? :P)

[1]
[https://github.com/petkaantonov/bluebird/commit/70f90c33dd94...](https://github.com/petkaantonov/bluebird/commit/70f90c33dd944a54d2995ed41f08860cc414f824)

------
Terr_
Originally I had milder "this could be a problem" objections, but now on my
Nth edit I've fallen into "are you _trying_ to drive us all insane" territory.

So here's the (shorter) list of issues, which I'll support later on with code
samples:

1\. The new behavior is invisible to most readings of the source code. It's
just magic, and unless you've obsessively tracked the magic as it flows, you
never know where it might be.

2\. The magic can strike in any place, at any time, and can _throw an
exception_ when it happens. Even `a=b+c` is no longer safe.

3\. Even when it doesn't throw an exception, the magic can cause
anywhere/anytime pauses and delays. Good luck with that smooth animation
function.

4\. The assignment semantics mean that even the equals sign is not safe, and
can fuck up my code.

5\. Infinite loops?

6\. Indefinite resolution?

______________

#1:

Should be self-evident. There's no way to know, reading code, whether a
variable is a future, unless you backtrack it _alllll_ the way. There are no
annotations or markings as it flows.

That would be par-for-the-course in JS and fine _except_ that we're not just
talking about another scalar type, this is a whole new beast.

#2:

    
    
        future function getCount(){  
            raise "OH GOD THE NETWORK IS DOWN";
        }
        var b = 7;
        var c = getCount();
    
        var a = b + c; // How was I supposed to expect an exception here!?
        

#3:

    
    
        var congratulationAnimation = function(msg){
            // Spend 1 second fading in different UI elements
            
            var escaped = escapeForHtml(msg);
            // Spend 1 second fading in message
    
            // Spend 1 second fading in remainder of UI elements
    
        };
        var msg = getMessage(); // Secretly a future from somewhere
        congratulationAnimation(msg);
        
        // WTF, why is my animation pausing halfway!?!? 
    

#4:

    
    
        var msg = future;
        alert(msg);
     
        // [...elsewhere...]
    
        // Store heroic status update
        msg = "Today I beat up a baby";
        msg += " godzilla which was terrorizing a local school";
    
        // OH GOD THEY THINK I'M A VILLAIN NOW
    

#5:

    
    
        // I have no clue what this'll do.
        var a = future;
        a = 1 + a;
    

#6:

    
    
        // What does this do, break console output forever?
        var f = function(){
            var a = future;
            console.log(a);
        }
        f();

------
Zelphyr
I've been actively programming in JS since it was in beta and this proposal
seems really confusing to me. I can only imagine what a new programmer would
think when they encountered `var a = future`.

And I know this seems petty, and for that I apologize, but... "excape"?

------
tlarkworthy
If synchronous looking code is switching on demand when IO gets blocked, then
isn't this just how other language schedulers work (e.g. python, green threads
in Java)?

A few years ago it's what everyone liked about node ...

------
Arkadir
A few years ago, I came up with a similar proposal, though more specifically
aimed at making callbacks easier to manage. It was a simple syntax extension,
as opposed to new semantics for the language.

You would write this:

    
    
        var! x = expression;
        statement;
        statement;
    

And JavaScript would parse it as:

    
    
        return expression.then(function(x) {
          statement;
          statement;
        });
    

This is obviously shamelessly pilfered from monadic syntax 'let!' in F#, and
my own 'let!' extension in OCaml.

------
venomsnake
I have been using Jquery promises with good enough success. And the chaining
makes the code synchronous to read.

Improve them slightly add error handling like maybe monad. Yield has done
wonders for the delegate allergic people in C# for ages.

We probably should abstain for a while to change the JS language - it is quite
messy as it is. Undescore is awesome example how you can extend the language
with small amounts of code.

------
niix
Not too sure if I agree with this. I'm glad the author noted that he wrote
this prior to knowing about es6 generators.

------
gerbal
This would be wonderful, especially for people learning JavaScript. When and
how to use call backs and wrangling asynchronous code propagation is a major
frustration coming to JavaScript from languages that default to synchronous
behavior.

~~~
GeneralMayhem
I'm of two minds on this front. On the one hand, managing asynchronous code in
JS is hard. On the other hand, managing asynchronous code _in general_ is
hard, and the only difference is that JS makes you think about what it is
you're actually doing. I feel like having to make such behaviors explicit in
JS has made me a better programmer in other languages because of having had to
think through the timing and sequencing issues. It's just like memory
management in, e.g., Java - it's not that pointers aren't there, it's just
that they're mostly managed for you, and knowing how they work is beneficial
to forming a better mental model.

------
ihenriksen
I use Q and its asynchronous promises for JavaScript works as promised :)
[https://github.com/kriskowal/q](https://github.com/kriskowal/q)

------
Touche
> If you ever had to port localStorage based code to IndexedDB you have been
> confronted to Javascript's biggest flaw; its asynchronous system is leaky.
> Once you have an asynchronous method, every other method that calls it must
> be asynchronous as well.

If I was writing localStorage I would use setTimeout or something and make it
async from the beginning. You should always assume async when any type of I/O
is happening.

~~~
fvdessen
Yes In deed, but I think it is very unfortunate that JavaScript forces you to
make your code more complex than it needs to just because the API might change
in the future :/

------
disdev
I guess I don't understand the significant advantage this would provide. You
can accomplish some of the same functionality with other control flow
libraries. Also, I could see the future declaration, with potentially
cascading effects, causing as much confusion in the code as callbacks.

The point of Node is that it is asynchronous.

~~~
Ironballs
The idea of continuations (which promises are) is to make an asynchronous
control flow _look_ like it is synchronous, but not _become_ synchronous. More
than anything, it is about readability and having a logical flow in the code.

------
ilaksh
ToffeeScript

