GGistDev

Async/Await in JavaScript

async/await lets you write asynchronous code that looks synchronous by awaiting promises.

Basics

Mark a function async and await promises inside. The return value is wrapped in a promise.

async function fetchValue() {
  return 42
}

async function main() {
  const v = await fetchValue()
  console.log(v)
}

Parallel vs sequential

Awaiting sequentially slows you down; start promises first, then await with Promise.all.

// sequential
const a = await getA()
const b = await getB()

// parallel
const pa = getA()
const pb = getB()
const [a2, b2] = await Promise.all([pa, pb])

Error handling

Use try/catch around awaits or handle with .catch().

try {
  const data = await getData()
} catch (e) {
  console.error('failed', e)
}

finally for cleanup

const res = await getRes().finally(() => release())

Top-level await

Supported in ESM (modern browsers/Node). Use sparingly to avoid blocking module graphs.

// in a module file
const config = await fetch('/config.json').then(r => r.json())

Timeouts and abort

Combine with AbortController to cancel fetches and with Promise.race for timeouts.

const c = new AbortController()
const p = fetch('/api', { signal: c.signal })
const timeout = new Promise((_, rej) => setTimeout(() => rej(new Error('timeout')), 1000))
const res = await Promise.race([p, timeout])

Pitfalls

  • Don’t forget to return/await inside async functions (unhandled rejections)
  • Don’t await inside array .map without collecting promises; use Promise.all
  • Avoid top-level await inside large module graphs

Summary

  • Use async/await for readable async code
  • Start work in parallel then await together; handle errors with try/catch
  • Support cancellation/timeouts where possible