Skip to content

Instantly share code, notes, and snippets.

@ncksllvn
Created April 9, 2018 20:09
Show Gist options
  • Save ncksllvn/714432956a0da995cfe3ce133e67dcbf to your computer and use it in GitHub Desktop.
Save ncksllvn/714432956a0da995cfe3ce133e67dcbf to your computer and use it in GitHub Desktop.

Async/Await

Allows you to write asynchronous code that appears synchronous.

Callbacks to Promises to Async/Await

Callbacks

Needs no introduction, because every JS developer knows the pain of trying to cleanly manage a series of async operations via callbacks alone.

To Promises

Until Promises became mainstream, we would generally wrap callbacks in a Promise so that they would work with a Promise chain. Then we could manage the operations in a more flattened manner like so:

// Imagine this as any block of asynchronous code that does something with an optional parameter.
function someAsyncOperation(offset = 0) {
  return new Promise(resolve => {
    setTimeout(() => resolve(offset + Math.ceil(Math.random() * 100)), 1000)
  })
}

// Three async operations, each one dependent on the result of the former
someAsyncOperation()
  .then(first => {
    return someAsyncOperation(first)
  })
  .then(second => {
    return someAsyncOperation(second)
  })
  .then(third => {
    console.log(third)
  })

To Async/Await

The await operator allows you to stop the execution of the current function until a Promise finishes execution. Any function that uses the async operator must mark itself as async in the function definition so that any code that utilizes it knows that the function is asynchronous, and can choose how to handle that.

The former example could be rewritten as:

function someAsyncOperation(offset = 0) {
  return new Promise(resolve => {
    setTimeout(() => resolve(offset + Math.ceil(Math.random() * 100)), 1000)
  })
}

async function main() {
  const first = await someAsyncOperation()
  const second = await someAsyncOperation(first)
  const third = await someAsyncOperation(second)
  console.log(third)
}

Async/Await concepts

You can await synchronous functions

// 50/50 chance of this function being async or sync, but await can be used either way
function maybeAsyncOperation() {
  if (Math.round(Math.random() * 2)) {
    return 100;
  } else {
    return new Promise((resolve) => { /** some async operation */ })
  }
}

async function main() {
  const result = await maybeAsyncOperation()
  console.log(result)
}

main()

You use try/catch

Instead of .catch as used in a Promise chain, you use the good old-fashioned try/catch with await, and any rejected Promise will fall to the catch block.

function mightThrowAnError() {
  if (Math.round(Math.random() * 2)) {
    throw new Error('The ship is down!')
  } else {
    return new Promise((resolve) => { /** some async operation */ })
}

async function main() {
  try {
    const result = await mightThrowAnError()
    console.log(result)
  } catch(err) {
    // Recover somehow!
  }
}

main()
}

It's still Promises

You don't have to immediately await the return value from a function. You can store the reference to the Promise and await it later. Or build an array of Promises, and await them all at once use await Promise.all(operations).

async function main() {
  const pendingOperation = maybeAsyncOperation()
  // ------------------------------------
  // do a bunch of things while that operation is in-progress
  // ------------------------------------
  const result = await pendingOperation;
  console.log(result)
}

Why mark Promises with async?

To the runtime, async can be considered a flag that there is an implicit Promise returned from your function that is resolved once your function is finished. It's also a flag to any higher-level function that to access your function's return value, or to ensure that your function is finished executing before continuing, you must use the await operator on it.

For example:

// Sends three distinct HTTP requets one by one, gets the JSON response value, and returns the values as an array.
async function doThreeRequests() {
  const results = []
  const requestUrls = ['/url1', '/url2', '/url3']
  for (const requestUrl of requestUrls) {
    const response = await fetch(requestUrl)
    results.push(await response.json())
  }
  return results
}

async function main() {
  // Despite the code for doThreeRequests looking synchronous, it's actually returning a Promise
  // that must be awaited in order to access doThreeRequests's return value.
  const [one, two, three] = await doThreeRequests()
}

main()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment