Live data from Hacker News

Mistakes we make using JavaScript Promises

betamark.com

31–40 of 60 posts

Re: Mistakes we make using JavaScript Promises

#31

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.

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.

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.

Re: Mistakes we make using JavaScript Promises

#32
post #27

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 t…

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.

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..?

Re: Mistakes we make using JavaScript Promises

#33
_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 */ } ```

Re: Mistakes we make using JavaScript Promises

#34

Hopefully with async/await we can all put this behind us. In my own projects, async/await has made improved readbility and reduced errors.

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 va…

It's easy to run tasks in parallel with just "await". Just start your tasks without await, store the promise in a variable, and then use await later when you actually need the value.

Re: Mistakes we make using JavaScript Promises

#35
post #10

Earlier quoted context omitted.

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.

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.

A bigger danger still would be:

  const userPromise = fetchUser(id)
  const itemPromise = fetchItem(itemId)
  // Do stuff, maybe even more async stuff
  const item = await itemPromise
  const user = await userPromise
Since those promises haven't been awaited until later in the code, they could throw and result in an unhandledRejection, which would be pretty bad. Promise.all is much safer since it instantly awaits both promises.

Re: Mistakes we make using JavaScript Promises

#36
post #32
post #27

Earlier quoted context omitted.

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.

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..?

[deleted]

Re: Mistakes we make using JavaScript Promises

#37
post #32
post #27

Earlier quoted context omitted.

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.

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..?

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'
    });

Re: Mistakes we make using JavaScript Promises

#39
post #32

Earlier quoted context omitted.

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..?

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(functio…

[deleted]

Re: Mistakes we make using JavaScript Promises

#40
post #32
post #27

Earlier quoted context omitted.

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.

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..?

It iterates over all key/value pairs. For each value, if it is a promise it waits for it and replaces it with the result, while non-promises are left as-is. This is a fairly common function to have in promise libraries.
Post reply on HN