
Missing the Point of Promises - tlrobinson
https://gist.github.com/3889970
======
sthatipamala
Not that it's terribly relevant but a clever reader may notice that a Promise
is a monad that encapsulates and stands in place of some to-be-done operation.

The .then() method is this monad's bind operator. It allows you to chain a
Promise through functions that do not have to know or care about how to accept
promises as arguments.

Hopefully this might help Javascript programmers who are curious about the
relevance of these esoteric functional programming ideas. They are in your
language too! (just with less frightening names)

~~~
eranation
Oh, yes, of-course, now it makes it perfectly clear!

Reading HN sometimes make me wonder how I'm still getting paid for writing
code / managing others that do so. Was software reinvented in the past few
years while I was sleeping? Is functional programming taking over the world?
Is understanding things like Monads and Promises now a must have for anyone
calling themselves a programmer / developer / software engineer / hacker?

~~~
sthatipamala
People are simply giving names for useful patterns that you can use to solve
certain classes of problems. You can still write great code without using them
and you can even use them without knowing what they are called.

Giving names to these ideas just makes it easier to communicate common
concepts to yourself and others. (imagine how hard it would be if you didn't
know the terms "binary tree" or "MAC address").

The existence of these concepts is not an affront to your existing knowledge.
Learn them only if you think it helps you.

~~~
Jare
The point of the article is that Promises should not be just the name of a
general pattern or idea for a common concept, but adhere to a very specific
definition. Monads as far as I know are already that, not a pattern but a very
specific type of construct.

That's one of the reasons I hate the reality of design patterns in modern
software: they start off as useful vocabulary, then some specific variants of
the pattern hijack the name, and soon it's impossible to communicate clearly
as nobody knows which variants have which names and meanings, and vocabulary
starts to diverge wildly (see also: MV* patterns).

~~~
brazzy
The worst example of that phenomenon is Singleton, which everyone believes is
about a static factory method that returns the instance. In fact, that was
only an EXAMPLE given in the GoF book for a mechanism to get the singleton
instance...

------
cletus
I've been writing a fair amount of AngularjS code recently and have been
dealing with this issue. Angular has a $q service [1] for deferred actions,
which is a cutdown version of the Q library [2]. I like it a lot.

The nice thing about Angular is that the use of promises is pervasive (eg the
$http service) and Angular understands them so:

    
    
        $scope.projects = $http.get('/projects');
    

and a template of:

    
    
        <ul>
          <li ng-repeat="project in projects">
            {{ project }}
          </li>
        </ul>
        

where $http.get() returns a promise just works.

[1]: <http://docs.angularjs.org/api/ng.$q>

[2]: <https://github.com/kriskowal/q>

------
eranation
I promise that when I will understand this, I'll call back.

But seriously, can someone please translate this to English? and this comes
from someone that writes JavaScript for 12 years (and have read the first 3
chapters of "JavaScript the good parts") I just couldn't keep up with the
article and ended adding it to pocket, (formerly read it later) which we all
know that it means I'll never read it again.

Is there a good article that will make all this suddenly clear to me? What was
so wrong with callbacks?

~~~
pygy_
This article made it click for me. I had read several others that went over my
head.

The point of promises, as explained in this article, is to mirror the error
handling semantics of synchronous code with minimal boilerplate.

In code based on callbacks, if an error occurs somewhere, you must either deal
with it locally or pass it along the callback chain manually. Promises mimic
the try/catch system.

Here's a variation based on the first example in the WhenJS documentation[0].
It demonstrates the basic control flow, we'll see later why it makes error
handling easier:

    
    
        function loadImage (src) {
            var deferred = when.defer(),
                img = document.createElement('img');
            img.onload = function () { 
                deferred.resolve(img); 
            };
            img.onerror = function () { 
                deferred.reject(new Error('Image not found: ' + src));
            };
            img.src = src;
    
            // Return only the promise, so that the caller cannot
            // resolve, reject, or otherwise muck with the original deferred.
            return deferred.promise;
        }
    
        // example usage:
        loadImage('http://google.com/favicon.ico').then(
            function gotIt(img) {
                document.body.appendChild(img);
                return aSecondPromise;
            },
            function doh(err) {
                document.body.appendChild(document.createTextNode(err));
            }
        ).then(function(/*resolved value of the second promise*/){...});
    

First, lets focus on the `loadImage()` function, which wraps an async image
load into a promise object.

It first creates a deferred object wich has two methods and one property:

* `defered.promise` is an objet with a `then` method that takes two optional handlers (success,error), which returns another promise. This allows chaining (more on that later).

* `defered.resolve(result)` resolves the corresponding promise: it takes one parameter and passes it to the success handler.

* `reject(error)` has similar behaviour but for the error handler.

These two methods are mutually exclusive. If you call one, you can't call the
other.

As you can see, `loadImage()` defines two event handlers for the img object,
that will either resolve or reject the promise. It schedules the async event,
then returns the promise object. The `then` method of the latter registers two
handlers one of which will kick in when the async call resolves the promise.

\--

There's no point in using promises for trivial code like this.

Their usefulness comes from subtleties in the way the `then()` methods
propagate the successes and errors when they are chained.

Success and error handlers are optional. If at a given step a success/error
should have been triggered but isn't, the value is passed to the next one of
the same kind in the then chain.

Let's take the example in this article:

    
    
        getTweetsFor("domenic") // promise-returning async function
            .then(function (tweets) {
                var shortUrls = parseTweetsForUrls(tweets);
                var mostRecentShortUrl = shortUrls[0];
                return expandUrlUsingTwitterApi(mostRecentShortUrl); // promise-returning async function
            })
            .then(doHttpRequest) // promise-returning async function
            .then(
                function (responseBody) {
                    console.log("Most recent link text:", responseBody);
                },
                function (error) {
                    console.error("Error with the twitterverse:", error);
                }
            );
    

At each step, the promise can be resolved or rejected. If it is resolved, its
result is passed to the next success handler. If if is rejected at any step,
the following OK steps are skipped, and the error ends up being handled by the
error handler defined in the last then clause. It is thus equivalent to this
synchronous code:

    
    
        try {
            var tweets = getTweetsFor("domenic"); // blocking
            var shortUrls = parseTweetsForUrls(tweets);
            var mostRecentShortUrl = shortUrls[0];
            var responseBody = doHttpRequest(expandUrlUsingTwitterApi(mostRecentShortUrl)); // blocking x 2
            console.log("Most recent link text:", responseBody);
        } catch (error) {
            console.error("Error with the twitterverse: ", error);
        }
    

Imagine how messy it would be to propagate the errors in standard callback-
based code...

\--

One last thing about handlers:

    
    
        loadImage(src).then/*1*/(successH, errorH).then/*2 */(successH2, errorH2);
    

loadImage() returns a first promise and its `then1()` method registers the
handlers and returns a promise, whose state will depend on the behaviour of
the handlers once one of them is triggered. If the handler (either successH or
errorH) returns a value, the promise will pass that value to the next success
handler. If the handler throws an exception, the value thrown will be passed
to the next error handler, and so on.

This brings us the following parallel, FTA:

1\. Promise fulfilled, fulfillment handler returns a value: simple functional
transformation

2\. Promise fulfilled, fulfillment handler throws an exception: getting data,
and throwing an exception in response to it

3\. Promise rejected, rejection handler returns a value: a catch clause got
the error and handled it

4\. Promise rejected, rejection handler throws an exception: a catch clause
got the error and re-threw it (or a new one)

At last, a handler can return a new promise. In that case, the handler that is
called next will depend on whether that promise is resolved or rejected.

\--

Sigh... I'm done. I hope I didn't make things more confusing.

\--

[0] <https://github.com/cujojs/when/wiki/Examples>

~~~
decadentcactus
Actually, you made it a lot clearer for me. Thanks for the writeup.

~~~
pygy_
Glad I could help. This post was mostly a stream of thoughts, and it contains
errors and approximations. I'm preparing a real article on the topic.
Hopefully ready in a few days.

------
thristian
Twisted (the asynchronous framework for Python) has an equivalent structure
called a "Deferred":

    
    
        http://twistedmatrix.com/documents/current/core/howto/defer.html
    

It's very similar to the "Promises/A" system described in the article, except
that it has different method names (instead of .then(), it has the more
prosaic .addCallback() and .addErrback()), and you generally have only one of
them (instead of the callback-adding function returning a new, fresh, clean
instance). Deferreds have the same ideal of composability, and the same
mapping to 'try/except' statements in synchronous code.

However, there's one particularly handy thing Deferreds do that I don't see
described in the article: when "fulfilledHandler" gets called, it can return a
value which gets passed to the next callback in the chain, _or it can return a
Deferred_, in which case the callback chain pauses until the new Deferred
fires and produces a value, at which point the original Deferred picks up and
carries on.

This comes in very handy for things like this:

    
    
        def getReservedBooksForUser(username):
            # Look up the auth token for the library service in the DB
            authTokenDeferred = db.runQuery("""
                    SELECT authToken
                    FROM users
                    WHERE username = ?
                """, (username,))
    
            # We know the DB result can only have one row and one column.
            authTokenDeferred.addCallback(lambda resultset: resultset[0][0])
    
            # When we have the auth token, use it when querying the library
            # service.
            authTokenDeferred.addCallback(
                    lambda authToken: web.getPage(
                        "http://library/books?authToken=%s&user=%s&format=JSON"
                        % (authToken, request["username"])
                    )
                )
    
            # Extract the list of books from the returned document.
            authTokenDeferred.addCallback(
                    lambda page: json.loads(page)['books']
                )
    
            return authTokenDeferred
    

Note that the second callback (the web.getPage() one) is immediately called
with the results of the first callback (the resultset-unpacking one), because
the first callback returned an ordinary value.

However, the third callback (the JSON-unpacking one) is _not_ called with the
Deferred returned by the second callback - instead, the callback chain is
paused while we wait for the second callback's Deferred to fire, then that
_result_ is passed to the third callback.

Is this kind of thing supported by JS Promises, too?

~~~
wycats
Good promises libraries (like Q and my RSVP.js:
<https://github.com/tildeio/rsvp.js>) absolutely do support this.

Check out [https://github.com/domenic/promise-
tests/blob/master/lib/tes...](https://github.com/domenic/promise-
tests/blob/master/lib/tests/returning-a-promise.js) for the tests for this
behavior, which most promises libraries (but not jQuery) pass.

------
optymizer
I've been using the 'async' lib in Node.js (I switched from 'step' lib) and I
find the Promises' .then() method chaining and exception throwing really
similar to async:

    
    
        async.waterfall({
            'step1':function(next) { next(null,1); },
            'step2':function(num,next) { next(null,num,2); },
            'step3':function(num1,num2,next) {next(null,num1+num2);}
        },
        function result(err,num) {
            if (err)
                console.error(err);
            else
                console.log("the number is:",num); //output: the number is: 3
        });
    

If any exceptions get thrown, the chain is interrupted and the last function
gets called with some err. That's just the 'waterfall' style. There are lots
of other methods that make the callback hell very manageable. In fact, I don't
know why I would use Promises over what async provides. Can anyone provide a
reason to, aside from _some_ syntax sugar?

~~~
domenicd
I have not dived into async, so it may have the ability to handle the
following and I just don't know it. But here are the things I _think_ it's
missing:

\- Interception of not only errors explicitly passed up to the callback, but
also thrown exceptions (see the paragraph containing "we take care of both
intentional and unintentional exceptions")

\- Per-task error interception, translation, and re-throwing

\- Guarantees like "never both fulfilled and rejected" or "the state never
transitions once it's fulfilled or rejected" or "only at most one handler is
called, at most one time". For example real bugs in popular libraries have
stemmed from calling callbacks with both an error and a result, or calling
them twice (with different parameters each time). A speaker at HuJS gave an
example of this in one of TJ Hollowaychuk's libraries.

~~~
optymizer
First and third are definitely true for async. I'm not sure about the second,
but it looks like something one would achieve with a try/catch block inside a
task.

------
skrebbel
It's lovely how Promises appear to be, in design, virtually equal to Tasks in
the .NET library. This should make them very easy for C# programmers to
understand.

`then()` is called `ContinueWith()`. .NET Tasks unfortunately lack the
progress callback. In turn, JS Promises appear to lack the ability to cancel a
running operation. Otherwise, they're nearly exactly the same.

I'd take this stuff over callback-passing anytime. I hope it becomes
commonplace.

~~~
domenicd
Yes, very true! In fact, many of the the Windows Runtime APIs are async. They
get projected identically into C# with `Task` as they do into JavaScript with
WinJS promises.

On cancellation, this is actually a feature of several promise libraries.
When.js has it, as do WinJS promises (so those cancellable Windows Runtime
APIs project just as well into JavaScript). We are hoping to add the feature
to Q, but we haven't figured out the exact semantics yet, especially with
regards to some of the object-capability security requirements Q attempts to
address.

Of course, C# 5 has a huge advantage in their `await` and `async` keywords.
But fear not! Shallow coroutines with `yield` are coming in ECMAScript 6 (the
next version of JavaScript), and you can build very similar support on top of
promises:

<http://taskjs.org/>

------
bad_user
If we are on the subject of promises, Javascript is a poor language to learn
about such things, no wonder developers understand the concept poorly.

The thing to understand about a promise is that _monadic operations_ apply,
which makes these instances _composable_ and in Scala at least you can do all
kinds of crazy things, like picking the first result that succeeded out of a
list of promises, or filter/map over such promises, operations which are also
async, producing other future results, or merging a bunch of future results
and passing the merged-result to another unit of computation that executes in
parallel and so on.

For Scala, there's an awesome Future/Promise implementation in Akka, soon to
be merged in Scala 2.10. You can watch this presentation by Victor Klang on
it: [http://skillsmatter.com/podcast/scala/the-future-i-was-
promi...](http://skillsmatter.com/podcast/scala/the-future-i-was-promised)

Note that in Scala, promises are actually named "Future", while a Promise is a
Future that you build (and complete) yourself (or put it another way, a Future
is an object from which you read, many times, while a Promise is an object to
which you write once). A Promise implements the Future interface.

And because Scala provides syntactic sugar for monadic operations, you can
write your code like this ...

    
    
        val googleResult = queryGoogleMaps(lat, lon)
        val bingResult = queryBingMaps(lat, lon)
        val yahooResult = queryYahooMaps(lat, lon)
    
        val bestLocation = for {
           location1 <- googleResult
           location2 <- bingResult
           location3 <- yahooResult
        } yield compareAndPickBestResult(location1, location2, location3)
    

Notice how this code could be completely synchronous, and yet if those
functions I used return some Future[Result], then it still works as intended,
with "bestLocation" being another Future result, so if you need it right now,
you'll have to wait for it ...

    
    
        Async.wait(bestLocation, 3 seconds)

~~~
recursive
> The thing to understand about a promise is that monadic operations apply

If that's necessary to understand, then javascript isn't the problem. Most
developers do not understand monadic operations.

~~~
bad_user
That's actually a pity, because monads are easy to understand, being just like
a normal design pattern ... name your favorite one and it's probably harder to
understand than monads, because monads are actually well defined.

I was also giving Scala as a better language to experiment with such things,
because the funny thing about Scala is that monads are more composable in
comparison to Haskell or other functional languages.

~~~
recursive
I've spent more time trying to understand monads than all the named design
patterns put together. And I don't have a favorite design pattern, just a
vague skepticism for the lot of them, and a strong skepticism for anyone who
promotes them.

Anyway, being well-defined is not a sufficient condition for being easy to
understand.

------
wahnfrieden
It's also worth looking at Dojo's Deferred, which is what jQuery copied for
its promises (and Dojo's itself copied the idea from Twisted). jQuery's
implementation is broken and spread out over several releases though.

------
avolcano
This article comes at a good time for me - was wondering how exactly I was
supposed to be using the Promises in WinJS (which are woefully undocumented in
terms of actual usage, as are, well, all things WinJS/Metro, unfortunately).

------
dizzystar
Are promises basically streams? Or am I over-generalizing here?

~~~
bad_user
Yes and no. You can have stream-like functionality (piping the response of one
promise to another), but that's not what promises are all about.

A promise at its core represents a result that will be available in the
future. The other thing you have to understand is that an instance of a
promise supports _monadic operations_ (i.e. filter, map, flatMap, depending on
the implementation of course, but without these monadic operations any
implementation is useless).

What does this mean actually? It means that these promises are _composable_.
Piping is only one usecase, however you can compose them in any way you want.

For instance, you can do something like ... execute tasks A, B and C in
parallel and from whichever one finishes first, from its result start tasks
D1, D2 ... Dn in parallel, drop whichever of the tasks timeouts or throws an
exception, then merge the results of the tasks that succeeded and pass it to
task E, which will produce a final response.

------
dos1
The author mentions jQuery's broken promises implementation and how it
prevents the excellent chaining feature. I definitely see how jQuery's
implementation differs from the Promises/A spec. However, doesn't .pipe() in
jQuery basically allow for the same chainability as .then() from Promises/A?
Is there a difference I'm not aware of?

~~~
domenicd
No, jQuery's `pipe` (or their `then` in >= 1.8) still does not do error
handling. I updated the article a couple days ago to address this directly:

\---

This breaks down into four scenarios, depending on the state of the promise.
Here we give their synchronous parallels so you can see why it's crucially
important to have semantics for all four:

\- Fulfilled, fulfillment handler returns a value: simple functional
transformation

\- Fulfilled, fulfillment handler throws an exception: getting data, and
throwing an exception in response to it

\- Rejected, rejection handler returns a value: a catch clause got the error
and handled it

\- Rejected, rejection handler throws an exception: a catch clause got the
error and re-threw it (or a new one)

Without these transformations being applied, you lose all the power of the
synchronous/asynchronous parallel, and your so-called "promises" become simple
callback aggregators. This is the problem with jQuery's current "promises":
they only support scenario 1 above, omitting entirely support for scenarios
2–4.

\---

So you get chaining, sure; you can flatten your callback trees into callback
lists. That's cool, I guess. But they're still missing the point, which is to
allow composability not only of always-successful functions, but of functions
that sometimes fail as well.

~~~
masklinn
> This is the problem with jQuery's current "promises": they only support
> scenario 1 above, omitting entirely support for scenarios 2–4.

Actually, `pipe` kind-of handles case 3 as well: if a promise is returned from
either `pipe` handler it's used as replacement for the `pipe`'s resolution (a
value merely replaces the existing resolution or rejection value), so

    
    
        promise.pipe(null, function () {
            return $.when(42);
        });
    

will replace a rejection of `promise` by a resolution to 42.

Also, there's a mistake with the text I believe:

> That means if you give a promise out to multiple consumers, they can
> interfere with its state.

No, if you give a deferred to consumers they can alter it but the result of
`.promise()` doesn't provide mutator methods, only state change callbacks.
Promise producers are supposed to return the result of `.promise()`, returning
a non-rejected and non-resolved Deferred directly is an error. And while — as
you note — Deferred#then doesn't behave per-spec, Deferred.pipe does create
and return a new promise.

(jQuery's deferreds have other issues which your post hasn't touched, such as
the very special handling of single-value resolutions.

