
Mistakes we make using JavaScript Promises - betamark
https://www.betamark.com/blog/mistakes-using-javascript-promises/
======
franciscop
This "solution" given here has a syntax error since you don't have access to
`user` within the second then() callback:

    
    
        get("http://data.com/user") 
          .then(user => get("http://data.com/location" + user.id))
          .then(location => createEntry(user, location)) 
          .then(response => {
            // handle response 
          }).catch(err => {
            // handle failure
          });
    

Instead, back before async/await made things easier this nested pattern was
used (notice the difference in parenthesis location):

    
    
        get("http://data.com/user") 
          .then(user => get("http://data.com/location" + user.id)
            .then(location => createEntry(user, location)))
          .then(response => {
            // handle response 
          }).catch(err => {
            // handle failure
          });
    

Edit for completeness, this is how it is now with async/await:

    
    
        try {
          const user = await get("http://data.com/user"); 
          const location = await get("http://data.com/location" + user.id);
          const response = await createEntry(user, location);
          // handle response 
        } catch (error) {
          // handle failure
        }

~~~
fooey
Instead of subtle nesting, I would have passed through the user as part of the
result of the second promise.

    
    
        get('//data.com/user)
          .then(user => resolve({user, location: get('//data.com/user')}))
          .then(({user, location}) => createEntry(user, location))
          .then(response => {
            // handle response 
          }).catch(err => {
            // handle failure
          });
    
    

Async/await is much cleaner now though.

~~~
korla
How does the resolve function work here? It is passed an object where one
property is an object and the other a promise. The next then suddenly receives
the resolved location..?

~~~
hoorayimhelping
The argument passed to `resolve` gets returned from the resolve function and
becomes available to any handler that handles it with `then`. In the above
example, the object is being destructured as part of the arguments. The
destructuring is probably what's tripping you up.

    
    
        // straightforward, no magic
        const simpleFunction = function() {
          return Promise.resolve({ name: 'simpleObject' });
        }
    
        simpleFunction().then(function(response) {
          console.log(response); // { name: 'simpleObject' }
        });
    
    
        // alternatively, destructuring the arguments
        simpleFunction().then(function({ name }) {
          console.log(name); // 'simpleObject'
        });

~~~
korla
No that is not what is tripping me up, or happening. The other answer
explained it, some library code was executed aeaiting the location-promise.

------
hn_throwaway_99
With respect to Promise.all:

> In this example, both promises will be processed asynchronously and only
> when both of them are resolved, we handle the result.

While that is true if all the promises resolve successfully, if _any_ of the
promises get rejected the catch block gets executed as soon as the _first_
rejection occurs. Beware of that behavior, as if you're not aware of it it can
leave your system in a bad state (speaking from experience, of course :)

------
ilaksh
Metamask doesn't like the domain:

"Ethereum Phishing Detection"

This domain is currently on the MetaMask domain warning list

------
hayksaakian
Hopefully with async/await we can all put this behind us.

In my own projects, async/await has made improved readbility and reduced
errors.

~~~
paulhodge
async/await helps a lot, but there are still a few common errors with them...

1) Not calling promises in parallel. Easy to do because it's impossible to run
them in parallel with just "await", need to use Promise.all() or something.

2) Forgetting to write "await". If you try to use the return value then now
you have a Promise object instead of the actual value. But the worst is when
the code doesn't use the return value. Then it has a really subtle race
condition that's hard to find. Or if the promise has any errors, then you
might get the dreaded "Unhandled promise rejection" with a useless stack
trace, and you might need to search the entire codebase to find where it
happened.

~~~
ng12
For the first I wonder if we could solve this with array syntax:

    
    
       const [foo, bar] = await [async1(), async2()]

~~~
simplify
Yes, the correct syntax is:

    
    
        const [foo, bar] = await Promise.all([async1(), async2()])

------
CraftThatBlock
Are these "common" mistakes? With basic understanding of Promises, these
mistakes shouldn't happen. Also I believe #1 is wrong when using async/await.

~~~
tehlike
Some older libraries will still be callback driven, and one important thing is
always resolving or rejecting promise.

~~~
CraftThatBlock
If it's callback driven, is it a Promise mistake or a callback mistake? I
don't think it would apply here

~~~
tehlike
Most of the time first thing i do is to wrap the library call in a promise.
Callbacks are really bad after a few nested ones, bht promise doesnt eliminate
all the problrms

------
seniorsassycat
_any_ function that returns a promise should have the `async` modifier - even
if `await` isn't used.

A function may return a promise or throw an exception. An `async` function may
only return a promise. async functions cannot throw exceptions.

``` // This code sucks but you might have to write it if `get` isn't an async
function. try { get().catch(_ => /* handle async errors _/ ) } catch { /_
handle sync errors */ } ```

~~~
matt-attack
Interesting about adding async. I actually didn’t know it eliminated the
possibility of it throwing an error. What happens if it explicitly throws an
error?

~~~
seniorsassycat
An async function that throws and error will return a promise that will reject
with the thrown error.

------
borschtplease
And he makes mistake #1 in mistake #3 by running „get“ sequentially instead of
using Promise.all

~~~
jchw
They're all artificial examples: one could assume that the tasks in #3 are
intended to be executed serially. Though there aren't any data dependencies
expressed between the different calls, perhaps ordering could be important for
other reasons.

~~~
TheCycoONE
I feel it should have been called out explicitly when introducing await,
because the 'clean' solution in async/await code is to call each async
function and then await the results where you need them - which is a pattern
he doesn't hint at at all.

~~~
jchw
I think there is a danger in that approach: if you forget to await, errors are
silently ignored. You can also await a Promise.all, which is reasonably good
enough if you do depend on all of the results to do anything meaningful
anyways.

~~~
TheCycoONE
If you need you can name your intermediate results fooPromise or something
else to make it obvious that it isn't meant to be used directly.

The async article linked from this article has a toy example for this concept
where the result is unused, but most of the time you would be passing the
result somewhere. If you were using typescript it could fail at compile time
when you try to pass a promise instead of the expected type. Even if you're
not, you should notice it never work in your tests.

~~~
jchw
Sorry, by forget to await, I mean you call an async function but then don’t
ever actually use the result. TypeScript can also diagnose that one by
illuminating unused variables.

Still, I think it is easiest to never mess up if you await as soon as you have
a promise value. In many common cases this is easy enough.

------
NohatCoder
Lots of bikeshedding about minute differences in syntax. Especially #3 rubs me
the wrong way, it is all syntactic sugar, the only tangible thing you get from
all this is to prevent the code from working in older browsers.

------
qazpot
I find Promises to be nothing but fancy looking syntax sugar over the
callbacks. Infact, using the Async library one can write more cleaner and easy
to understand code using callbacks as compared to Promises.

------
betamark
Thanks for all the feedback! I will correct the article shortly

------
acjohnson55
One thing I realized about async/await is that it removes the ability to use
the synchronous continuation of an async function call. In return, it makes
its asynchronous continuation feel synchronous. Technically, that's less
power, but I realized that's almost always a good thing.

~~~
hn_throwaway_99
I don't understand what you're saying here. An async function just returns a
Promise, that's it. You don't have to await on it immediately - you could just
as easily assign it to a variable and await on it some time later.

~~~
acjohnson55
I should really say `await`, in particular. But obviously, you can't `await`
outside of an `async` function (proposed top-level `await` notwithstanding).
It's true that you'll eventually get to the top of the `async` function stack
and you'll have an actual Promise value, at which point, you're back in
synchronous continuation land.

But point is that using `async`/`await` as much as possible restricts the
programmer to less concurrency, which inevitably means fewer glitches and race
conditions. Many people make the mistake of thinking that JavaScript is safe
because there's only one thread. But it turns out that most of the hazards of
concurrency still exist as long as continuations can interleave with access to
shared resources and mutable state.

Does that make more sense? It's late over here, so I might not be super clear.

