
We have a problem with promises - nolanl
http://pouchdb.com/2015/05/18/we-have-a-problem-with-promises.html
======
falcolas
Callback heavy languages can be very challenging to think about. They require
keeping quite a few disparate pieces in your mental stack all at once.

Promises make this both better and worse, in my experience. In the easy cases,
all you have to do is follow the promises syntax, and you can treat it as
linear code.

The problem arises when writing non happy path code, you not only have to
think about the promises syntax, but about the abstraction it's creating over
callbacks. Otherwise, you begin to miss corner cases, create incorrect
promises, and in general write code which misbehaves (often silently).

Promises are powerful abstractions, but like all abstractions, they have their
leaks. Also like all other abstractions, there are places where you have to
fully comprehend the mechanisms underneath them to use them properly.

~~~
Bahamut
I like doing stuff like

    
    
      function ensureFoo() {
        let promise = Promise.resolve();
        
        if (!foo) {
          promise = getFoo();
        }
        
        return promise;
      }

~~~
Guillaume86
Shoudn't you store the initial fooPromise and use it instead?

    
    
        var ensureFooEquivalent = getFoo();

~~~
Bahamut
Sometimes the data you want is already available - no need to make an extra
request in those cases.

------
adrusi
The real confusion here is dynamic typing. It's weird that the parameter to
.then() can return any value, but if it's a promise there's special behavior.
And for no good reason either

    
    
        p.then(r => r + 5);
    

Is just a bit of sugar for

    
    
        p.then(r => Promise.resolve(r + 5));
    

And then the type signatures are easy to reason about. The dynamic typing also
risks introducing hard to find bugs. Suppose you have an heterogeneous array
`things` which might contain numbers and promises which will resolve to
numbers.

    
    
        p.then(r => things[r])
         .then(thing => selectedThings.push(thing));
    

You might intend `selectedThings` to also contain either numbers or promises
that resolve to numbers, ordered deterministically as a function of `p`, but
instead it will contain only numbers and its order is nondeterministic (it
depends on `p` and all of the promise members of `things` that `p` ever
signals).

~~~
Guillaume86
I don't agree with your first remark: what do you expect to be returned when
you return a value not wrapped in a promise?

The ambiguity is actually the auto unwrapping of promises for subsequent then
calls but I've yet to see a case where it's a problem (it's actually what
makes the API clean looking when used).

For your last snippet, I don't see an array so I don't know what ordering you
want to preserve (p always returns the same value so r will always be the
same), if it's actually inside a loop, you should use Promise.all or
Promise.map.

~~~
andrus
I think the confusion stems from the fact that .then() acts like either .map()
or .flatMap() depending on the return type.

If .then() was just .flatMap(), you could expect an error if you didn't return
a Promise (for example, reject with a TypeError).

If .then() was just .map(), you could return a value of any type (for example,
string or Promise<number>) and get back a Promise for a value of that type
(Promise<string> or Promise<Promise<number>>).

~~~
acjohnson55
Actually, it's slightly more complex because if the result of the function
passed to then were somehow a Promise<Promise<something>>, it would be
flattened recursively so that the ultimate result is just Promise<something>.
(Of course, due to .then's flattening semantics it's highly unlikely you'd
have a Promise<Promise<...>> in the first places).

The distinction between map and flatMap makes sense for, say, lists, because
you often do want to keep the non-flattened structure around. From promises, I
can't think of a reason why that would be desired. You just want to represent
the final result after all necessary waiting is completed. I suppose a library
could provide stricter map and flatMap for people who deeply care about this.

------
vkjv
> Rookie mistake #3: forgetting to add .catch()

I disagree with this advice. I usually refer to this as a "Pokémon Catch"
(Gotta' Catch 'Em All!). Never catch a promise that you are un-able to handle.
It's better to use a Promise library that supports debugging possibly
unhandled exceptions (e.g., Bluebird).

> Rookie mistake #4: using "deferred"

This one is interesting because it's actually used in the Q library
documentation for a way to `denodeify` or `promisify` a function. You
definitely shouldn't do this if it's already a promise, but outside of that,
it's more of a gray area. I tend to recommend using libraries to convert
callback functions to promises.

~~~
phs2501
The problem with "var d = defer(); ...; return d.promise;" vs "return new
Promise((resolve, reject) => {...});" is that the former does not protect you
from synchronous throws. In the latter (at least with every promise library
and native promise implementation I've seen) if the code in '...' throws, it
rejects the promise with the thrown error. In the former, it throws
synchronously, which especially in node is almost never what you want.

Besides, if you need to promisify a node-like function you should just use
Bluebird's promisification features; no point in doing it yourself. The fact
that Bluebird is also the most debuggable and performant Promise
implementation (including native) is just icing on the cake.

------
greggman
I would argue that `Promise.all` is not the equivalent of `forEach` because
Promise.all will execute everything immediately, maybe all will fail, maybe 1
will fail, eventually you'll have your then or your catch called but 100 of
your actions will have an attempt to be executed.

Compare that to forEach, if one fails and you throw the rest don't get
executed.

I suppose if whatever you're doing for each thing is async then they are
equivalent equivalent but in general forEach has different sementics than
Promise.all

~~~
MasterScrat
Now I wonder what would be the good way to make a forEach that actually
returns when all elements have been executed?

~~~
dcherman
Bluebird has this built in. For a quick userscript where I needed something
similar, I created this which works ( warning: no unit tests, but it works for
me )

[https://gist.github.com/dcherman/4dfba0d72c008ee5b59d](https://gist.github.com/dcherman/4dfba0d72c008ee5b59d)

It's a reduce function, but it could work as a forEach as well.

------
STRML
Great article; a few things I would add. I use bluebird for Promises, which is
just the most fantastic Promises lib ever conceived, no joke; if you haven't
used it try it. So some of these may be Bluebird-specific:

1\. Don't wrap callback functions manually with `new Promise(function(resolve,
reject) {...})`, just use `Promise.promisify(...)`. For one-off functions, try
`Promise.fromNode(function(cb) { fs.readFile('..', cb); });`.

2\. This pattern:

    
    
      getUserByName('nolan').then(function (user) {
        return getUserAccountById(user.id);
      }).then(function (userAccount) {
        // I got a user account!
      });
    
    

Could be:

    
    
      getUserByName('nolan')
      .get('id')
      .then(getUserAccountById) 
      .then(function (userAccount) {
        // I got a user account!
      });
    

3\. I too used Promise.resolve().then(...) to start a lot of route handlers.
Try `Promise.try(function() {...})`, which is equivalent but reduces the
temptation to just stick a synchronous value in the `Promise.resolve()` just
because you can.

4\. `Promise#nodeify()` is _super_ useful for creating functions that return
promises or use callbacks depending on how they're called. For example:

    
    
      function getUserName(id, cb) {
        return db.getUserAsync(id).get('name')
        .nodeify(cb);
      }
    

Is the same as:

    
    
      function getUserName(id, cb) {
        var promise = db.getUserAsync(id).get('name');
        if (!cb) return promise;
        promise.then(function(result) { cb(null, result);})
        .catch(function(e) { cb(e); });
      }
    

This is great if you want to convert a few functions you use to promises, but
they're called elsewhere and expect a callback style.

I'm sure there are many more. This is my bible:
[https://github.com/petkaantonov/bluebird/blob/master/API.md](https://github.com/petkaantonov/bluebird/blob/master/API.md)

In short; use Promises! They are the answer for callback hell in Node.
Async/await may fix more problems in the future but if you want Node/Browser
compatibility and to get started right now, Promises are the best way to go.

~~~
nolanl
Bluebird is awesome. :) I especially love promisifyAll() and promisify().

The only reason I didn't include something like that in the post is that I
didn't want to overwhelm newbies. I think it's already confusing enough what
the difference is between q/when/RSVP/bluebird/jQuery promises/Angular
promises, etc... And honestly, more and more I just use vanilla promises, or a
tiny polyfill like Lie.

~~~
ddoolin
After native promises, is there much reason to use 3rd party libraries?
Promises are a pretty small set of tools, so I'm not sure what one would have
to offer over another.

~~~
inglor
Bluebird is much faster and easier to debug than native promises. It is very
feature rich and it runs _everywhere_. It also converts callback APIs to
promises at one go.

That said, on a client-side app there are cases where I'd use native promises.
On the server - not a chance.

------
davexunit
The messes that callback/promise-heavy JavaScript make are a good example of
why we need syntactic abstraction in the language. With a proper macro system,
this whole async mess could be abstracted away and we could write nice linear
code.

~~~
mbrock
In Scheme, for example, a solution would be likely to involve delimited
reified continuations and not need much in the way of macros. It's not obvious
to me that the problem can be solved accurately with just syntactic macros.
Would the solution involve transforming code into CPS?

~~~
davexunit
Yes, delimited continuations would be a big part of the solution in Scheme.
The macros would just be the sugar on top of the whole system such that you
didn't have to write out all of the lambdas and prompting and aborting of the
continuations. I've implemented a coroutine system this way and it works
nicely.

------
johnnymonster
its interesting that when people talk about the newest feature or framework in
javascript, they tend to forget about javascript's core functionality. It's
always necessary to look at things like promises with javascript's core
functionality in mind. Take #3 for example, which is really just a function of
the core way javascript executes, taking promises aside!

You would not do something like this would you in a normal day at the office?
function myFunction(doSomething()){

};

so why would you do this.

Promise.then(doSomething());

doSomething() gets immediately invoked when the code is evaluated. It has
nothing to do with Promises.

Don't forget the fundamentals!

~~~
robmcm
This article has a lot of ambiguity which I think adds to the confusion.

For example I was looking at that question(3) thinking, does doSomethingElse()
return a function, another promise, nothing?

Part of the problem of having a very forgiving, lose language and API is that
it leads to hidden, subtle complications, highlighted by "Advanced mistake #5:
promises fall through". Here the then() method accepts anything as a return
type, but will only chain a function that returns a promise, not a promise
it's self. This isn't present in the MDN documentation
([https://developer.mozilla.org/en-
US/docs/Web/JavaScript/Refe...](https://developer.mozilla.org/en-
US/docs/Web/JavaScript/Refe...)).

~~~
vkjv
This. That was my "answer" to the question. "Oh, if `doSomethingElse()`
returns a function, it will run that function after the promise resolves
successfully."

------
LordHumungous
>If you know the answer, then congratulations: you're a promises ninja

No... you know how functions work in javascript.

~~~
simonw
Understanding functions in JavaScript is not enough to be able to complete the
quiz. You also need to understand the somewhat surprising semantics of the
Promises .then() method.

~~~
carussell
You don't.

Just to drive this home: I've read the spec a few times when evaluating
whether to integrate the concept into code I've written, soon after the
pattern was introduced, and then again a year or so later. (Side note: the
result of both evaluations was a "no thanks".) But at this point, I've never
used or authored a Promise, ever. And before moving past the intro that
contains the quiz in question, it's been over a year before I last looked at
them, much less read any up-to-date spec.

The quiz in the intro doesn't actually do a good job of exercising the details
of `then`, because they're all trivial; there's only a single use of `then` in
each example, rather than a chain of multiple ones (or a chain involving
`then` and `catch` for a less useful example). You can get past the quiz in
the intro by having only a general understanding of how `then` works. In fact
(and I can only speak for myself here, but this is how it played out when I
went through it), _not_ having an extensive familiarity with Promises may help
for the quiz, because that knowledge isn't crowding your thinking, and all you
see is function calls. The author points out the third example from the quiz
as particularly sticky, but it was the easiest gotcha to catch with my mostly
Promise-free background, because the effect where overlooking parts that can
lead you astray (because you expect idiomatic patterns in that place) is
pretty totally nonexistent.

It's the examples further into the article that you need to really look into
and consider how `then` works. For example, in rookie mistakes #2 and #5,
understanding how `undefined` is handled required backtracking to the intro
where the spec is linked, in order to delve into interaction with
"synchronous" return values, and `undefined` in particular.

Lesson for potential interviewers: don't rely on the intro quiz in this
article for your fitness function to weed out candidates, because it could
mean you're optimizing for the people you _don 't_ want (like me), rather than
those you do. _Do_ make sure to focus on the examples further in that actually
exercise an understanding beyond the superficial.

~~~
plorkyeran
I was able to answer it correctly with no knowledge of JS promises at all,
although I have used them in other languages. Being told that there's
something tricky going on was enough for me to be able to guess the difference
between #1 and #2, and all I needed to know for #3 vs. #4 was that promises
aren't a language extension that can capture expressions as thunks.

~~~
carussell
Thanks for corroborating!

------
xtrumanx
What's so bad about using deferred? In one case I call the resolve or reject
parameters and in the other I call a resolve or reject properties on the
deferred object. Not much of a difference to me.

I learned about deferred a few years ago and kinda stuck with it. It's all
over my code base and he didn't really justify why I should go about changing
it. The only thing I can reason I can think of is using his recommendation
follows the ES6 spec which doesn't matter to me that much for now.

~~~
Lio
The main issue I've found is with the way that deferreds handle then failures
and how "then" works.

According to the Promises/A+ spec, then should always return a new promise.
With the jQuery deferred.then() that's not the case.

In practice this means that it's harder to recover from failures as you're
processing your results because your all your onRejected callbacks are
attached to the same deferred and so will all fire one after another.

If you're just dealing with one asynchronous event, for example an AJAX call,
that might not be a problem but it can cause problems debugging more complex
code if you're not very careful.

E.g. (Get request) -then-> (onGetSuccessProcessor, onGetReject) -then->
(onProcessSuccess, onProcessingReject)

If the get request fails the onProcessingReject will also execute even though
we haven't run the onGetSuccessProcessor callback.

In Promises/A+ code you can return a new resolved promise from your onRejected
callback and the next "then" will fire it's onResolve callback instead of
onRejected.

Hope that helps. (it's a lot easier to draw as a petri net than to explain in
a comment on HN :)

~~~
dcherman
Be careful with that assertion. jQuery's `.then` method has returned new
Promises since 1.8.

In jQuery 3.0, their Promises will be fully Promises/A+ compliant as long as
you're using `.then` ( `.done` and `.fail` are remaining non-compliant to
remain non-breaking with sync XHR I believe )

------
tel
Re "Advanced Mistake #4", do Javascript programmers not know about tuples?
Does nobody write functions with signatures like

    
    
        pair(pa: Promise<A>, pb: (a: A) => Promise<B>): Promise<{fst: A, snd: B}> {
          
          return pa.then(function (a) { 
            return pb(a).map(function (b) {
              return {fst: a, snd: b}
            })
          })
    
        }
    

It's a super reusable little chunk of code.

~~~
phs2501
It is, but it only works for pairs and then you wind up with the relatively
nonsensical 'fst' and 'snd' names.

My preference would be:

    
    
      let user = await getUserByName('nolan');
      let userAccount = await getUserAccountById(user.id);
      // stuff with user and userAccount
    

But I have long since decided that I am never writing ES5 again, and since I
therefore need Babel anyways I may as well turn 'stage: 1' on and use
async/await. It's basically do-notation for promises.

~~~
tel
So alpha convert your names to whatever you like. I don't know how to do it
namelessly in Javascript without Chuch encoding the pair.

You can go much further than this. The following operation

    
    
        ap<A, B>(pf: Promise<(a: A) => B>): (pa: Promise<A>) => Promise<B> {
          return (pa: Promise<A>) =>
            pf.then((f) => pa.then((a) => Promise.resolve(f(a))))
        }
    

can be used to extend the previous idea as far as you like.

~~~
phs2501
I'm having trouble grokking whatever type-added syntax you're using for
Javascript here. I'm guessing it's:

    
    
      function ap(pf) {
        return (pa) =>
          pf.then((f) => pa.then((a) => Promise.resolve(f(a))))
      }
    

Still, if I wanted to go full-hog static-typed functional I doubt I'd be using
straight Javascript, I'd probably try one of the various Haskell-alike to JS
converters. Promises with async/await syntax seems like the most practical of
the various solutions whilst still feeling like Javascript.

(Besides, I prefer dynamic typing and I find points-free style in Haskell to
be obfuscating rather than clarifying, so it seems unlikely we're going to
have the same opinion of the best way to accomplish things here. :)

~~~
tel
Sorry, I'm just writing in Typescript. It's the typed Javascript flavor I'm
most familiar with. But, yep, you properly erased the types.

I'd love to use Haskell-to-JS but I don't think I could bet a product on it
quite yet.

------
nolanl
If anyone's confused by the 4 "puzzles," I whipped up a JSBin to demonstrate:
[http://jsbin.com/tuqukakawo/1/edit?js,console,output](http://jsbin.com/tuqukakawo/1/edit?js,console,output)

~~~
eevilspock
What if the doSomethingElse in #3 returns a function that is equivalent to the
doSomethingElse of #1? Shouldn't #3 then be identical to #1?

~~~
nolanl
Yes, but in most code I've seen, that's a typo rather than someone
intentionally writing a function that returns a function. You can definitely
do it!

------
arxpoetica
I made the transition from callbacks to generators/iterators recently, and I'm
really enjoying the yield/next foo. Promises just never really spoke to me.
Not certain why I always felt reticent to use them.

~~~
naturalethic
Yield works with promises too.

------
embwbam
Typescript or Flow will catch many of these errors. Highly recommended!

~~~
tel
Also having promise libraries which stuck closer to the basic monadic
interface without lots of overloading would help things like Typescript and
Flow catch all of the errors.

------
tel
And this is why types are nice. Along with limited overloading.

------
neumino
Gosh, adding a `.catch(console.log.bind(console))` is just insane.

If you have a library that will return non operational error, just remove it
from your project. If your code throws when it is not expected to, fix it.

This is like saying put a `try/catch(ignore)` everywhere. Seriously.

~~~
ben336
Logging errors to the console can be helpful to find them during
debugging/development (as opposed to having them fail silently).

The goal isn't to leave errors in, its to expose them when something changes
to make them happen so that you can do the 2 things above.

~~~
esailija
You need to configure or change your promise implementation if uncaught errors
fail silently.

In any case, logging error gives you no information anyway. Just try running:
`console.log(new Error("..."))`. So at the very least you should do
`catch(function(e){console.log(e.stack);})`

------
mweibel
Very nice article, will keep that in mind when trying to help others with
promises. Also helped me to re-understand some things :)

One thing though, Advanced mistake #4, is in my opinion good answer, the Q
library however gives a (afaik) non-a+-standard way of doing that which I
like:

from:

    
    
      getUserByName('nolan').then(function (user) {
        return getUserAccountById(user.id);
      }).then(function (userAccount) {
        // dangit, I need the "user" object too!
      });
    

to:

    
    
      getUserByName('nolan').then(function (user) {
        return [user, getUserAccountById(user.id)];
      }).spread(function (user, userAccount) {
        // I do have the user object here
      });

------
tumbling_stone
The most comprehensive and foolproof way is to grab the spec, read the
algorithm and fiddle around a day. Sadly this is the only way of fully
understand promises, promises already put a lot of cognitive load on your
brain when you're using them, so having any other abstractions of your own
(for remembering how promises work) is bad idea. IMO you're better off
investing a large continuous block of time for understanding promises rather
than reading some article here and there.

------
janfoeh
I am stumbling over this:

> Just remember: any code that might throw synchronously is a good candidate
> for a nearly-impossible-to-debug swallowed error somewhere down the line.

Why would a synchronously thrown error be swallowed, and why would I not just
`try { } catch` here?

~~~
nolanl

      naivePromiseAPI() {
        if (foo) {
          throw new Error('this will get swallowed!');
        }
        return somePromise();
      }
    

Instead you want this:

    
    
      cleverPromiseAPI() {
        return Promise.resolve().then(function () {
          if (foo) {
            throw new Error('this won't get swallowed!');
          }
          return somePromise();
        });
      }
    

Because if the client of the API does something like this:

    
    
      $('.my-button').on('click', function () {
        cleverPromiseAPI().catch(console.log.bind(console));
      });
    
    

Then the client might expect the error to get console.logged, but actually it
won't in the naive case. That's because the error was thrown synchronously and
thus bubbled up to the click handler, instead of being caught inside of the
promise chain.

~~~
janfoeh
Ah, I get it now, thanks! I missed the part about doing that for synchronous
code _in promise-returning APIs_.

I had a moment of stupidity and first somehow understood that to be a general
suggestion for error handling.

------
ThrustVectoring
Puzzle number 3 doesn't have a complete explanation. DoSomethingElse() can
return a function, which is then evaluated with the result of the first
promise as an argument.

------
tlrobinson
As soon as you can reasonably introduce an ES6/7 transpiler into your
toolchain you should start using ES7's async/await or equivalently ES6's
generators + a coroutine library function like Bluebird.coroutine, Q.async,
co, or Task.js.

It solves basically all of the problems mentioned in this article.

------
adhambadr
hahahahaha this one cracked me up "Writing code without a stack is a lot like
driving a car without a brake pedal: you don't realize how badly you need it,
until you reach for it and it's not there."

~~~
Kenji
I didn't really understand that one. First of all, I don't see why a stack
really is that indispensable, and secondly, JavaScript does actually have a
stack so I don't know how he thinks it's operating stackless - where do the
local variables go?

~~~
jerf
In the classic, but widely misunderstood, "Goto Considered Harmful",
Dijkstra's point is basically that structure programming is good because it
produces a stack and an instruction pointer, which contains an immense
quantity of useful information when a crash occurs. Goto is bad in this paper
because when it is used as the _only_ control flow construct, in a way
difficult for a modern programmer to understand in a world where structured
programming won so thoroughly that _every_ language is structured, it produces
no stack trace. You get a "Crash On Line 28430", with no clue how you got
there.

Naive event-based code is _somewhat_ like a goto, in that every time an event
comes in, you get a new stack constructed for that event. While you then get a
stack if you call functions from the initial handler, every event that comes
in whacks the stack afresh. So it isn't "the same as" a goto, but it does hurt
the stack. Instead of a stack frame that represents the whole of the program,
you get a series of "island" stack frames.

In some sense, this is the most fundamental reason why event-based programming
is so painful, and it's the problem that all these multitudinous programming
"async" techniques are trying to get around. Promises, for instance, can be
looked at as a partial way of having a sort of stack around a particular
sequence of operations that may be interrupted in arbitrary locations by this
event-induced stack discarding. In this sense they can also be viewed as an
inner-platform problem; where the language has "try/catch", for loops, etc,
promises end up trying to recreate all that in a second layer above the bottom
layer, with all the corresponding complexity of embedding a language in the
language.

If you then end up asking, "Well, what if we just didn't discard the stack in
the first place?", you end up in Erlang/Go/Haskell/Scala land.

------
jjaredsimpson
If devs don't want to read specs and instead read blog posts then its obvious
why there is a problem.

~~~
mistercow
Your snark would be appropriate if humans were logically omniscient, but we're
not. Reading and comprehending a spec does not imply grokking all of its
consequences.

