Search code examples
javascriptnode.jsasynchronousio

Where to put await?


I'd like to learn if there is a right answer of where to wait for async operations. If this is a topic with depth I'm also happy to receive articles or programming-terms to read up on.

Let's say we have a slow async operation, perhaps reading a large file:

const resolver = (x) => {
  return new Promise(resolve => {
    setTimeout(() => {resolve(x)}, 500)
  })
}

I can see two ways to use resolver:

const one = async () => {
  const result = await resolver('foo')
  console.log(result)
}

const two = async () => {
  const result = resolver('foo')
  console.log(await result)
}

(Note the differently placed await keyword)

In one we immediately wait, and this is the pattern I've been exposed to. I think it's the normal and expected pattern?

But should that line really wait for the result? It doesn't really need it resolved, right? result is only really consumed on the next line.. so why not wait there, as two illustrates?

There's obviously no gain in clarity for this trivial example, but in a larger function with multiple awaits and multiple consumers of the asynchronous result, I can maybe see it will be clearer to have the await colocated where it is actually required, rather than an upfront wait just because later code needs it.

Any right answers? Or thoughts? And if this is all a giant waste of worry then I'm happy to learn of that too :)


Solution

  • You're absolutely right. In larger pieces of code, where you put await matters.

    The following is an example of how the two styles lead to completely different interpretations:

    // Example 1 - serial async operations
    
    async function () {
        var a = await foo();
        var b = await bar();
    
        console.log(a+b);
    }
    

    Assuming that foo and bar each takes one second to return results, the above code will take roughly two seconds to execute.

    // Example 2 - parallel async operations
    
    async function () {
        var a = foo();
        var b = bar();
    
        var aa = await a;
        var bb = await b;
        console.log(aa+bb);
    }
    

    Making the same assumptions as the first example, the above code would take roughly one second to execute because bar will be executed in parallel without waiting for foo to complete.