async
await
programming
concurrency
JavaScript

Pros and cons of async/await

Interview Questions practice on Codemia

Over 8,000 real interview questions from top companies, searchable by company and role.

Browse interview questions

Introduction

async and await are popular because they make asynchronous code look much closer to ordinary control flow. That readability improvement is real, but it does not remove the underlying concurrency model or the need for careful error, timeout, and cancellation design. The best way to evaluate async and await is to separate what they improve from what they do not solve.

Main Advantages

The biggest advantage is readability. Dependent asynchronous steps can be written in a straight line instead of nested callbacks or long promise chains.

javascript
1async function loadUser(userId) {
2  const userRes = await fetch(`https://jsonplaceholder.typicode.com/users/${userId}`);
3  const user = await userRes.json();
4
5  const postsRes = await fetch(`https://jsonplaceholder.typicode.com/posts?userId=${userId}`);
6  const posts = await postsRes.json();
7
8  return { user, posts };
9}
10
11loadUser(1).then(console.log).catch(console.error);

That is easier to scan and maintain than many equivalent callback-based versions.

Better Error Handling

try and catch map naturally onto awaited operations.

javascript
1async function loadSafe() {
2  try {
3    const res = await fetch("https://jsonplaceholder.typicode.com/todos/1");
4    return await res.json();
5  } catch (err) {
6    console.error("Request failed", err);
7    throw err;
8  }
9}

This is one of the strongest reasons async and await are widely preferred in ordinary application code.

The Biggest Downside: Accidental Serialization

The main risk is that readable sequential syntax can trick developers into writing slower code. Independent operations are often awaited one after another when they should be started together.

javascript
1async function slowVersion() {
2  const a = await fetch("https://jsonplaceholder.typicode.com/todos/1");
3  const b = await fetch("https://jsonplaceholder.typicode.com/todos/2");
4  return [await a.json(), await b.json()];
5}

If those requests are independent, concurrency is better.

javascript
1async function fasterVersion() {
2  const [a, b] = await Promise.all([
3    fetch("https://jsonplaceholder.typicode.com/todos/1"),
4    fetch("https://jsonplaceholder.typicode.com/todos/2"),
5  ]);
6  return Promise.all([a.json(), b.json()]);
7}

So await improves readability, but it does not automatically produce efficient concurrency.

Cancellation Is Still Separate

async and await do not solve cancellation by themselves. If a request becomes irrelevant, you still need an explicit mechanism such as AbortController.

That means the syntax simplifies control flow, not lifecycle management.

Async Code Still Requires Discipline

The code may look synchronous, but it is not. Shared state, ordering, retries, partial failure, and timeouts still need real design decisions.

Teams still need clear rules around:

  • when to run tasks sequentially,
  • when to parallelize,
  • how to propagate or aggregate errors,
  • how and when to cancel work.

Readable syntax can hide those questions if the team is careless.

Where async And await Fit Best

Use them when:

  • one step depends on the previous result,
  • you want straightforward try and catch control flow,
  • the alternative would be nested promise chains.

For event streams, heavy concurrency orchestration, or reactive pipelines, promise combinators or stream abstractions may still communicate intent better.

A Good Team Rule

Use async and await for dependent workflows, then reach for Promise.all or a stream abstraction when concurrency itself is the core requirement. That simple rule prevents a lot of unnecessary debates about syntax versus architecture.

Common Pitfalls

  • Awaiting independent work sequentially and slowing the program down.
  • Assuming synchronous-looking code means concurrency issues disappeared.
  • Forgetting that async functions always return promises.
  • Ignoring cancellation and timeout requirements.
  • Mixing await, callbacks, and raw promise chains inconsistently in one control path.

Summary

  • 'async and await greatly improve readability for dependent asynchronous workflows.'
  • They also make error handling feel more natural in many cases.
  • They do not eliminate concurrency, cancellation, or timing complexity.
  • Used carelessly, they can serialize work that should run in parallel.
  • Treat them as a control-flow tool, not as a complete async design solution.

Related reading
Free course
Beginner
7 lessons
2 hours
Tackling System Design Interview Problems

A short course that equips you with the skills to approach system design interviews methodically.

Start the free course
Track what you have practised

A free account saves your progress, solutions and study plan across every problem on Codemia.

Interview Questions practice on Codemia

Over 8,000 real interview questions from top companies, searchable by company and role.

Browse interview questions

All Rights Reserved.