async/await
JavaScript
error handling
asynchronous programming
troubleshooting

Why async/await doesn't work in my case?

Master System Design with Codemia

Enhance your system design skills with over 120 practice problems, detailed solutions, and hands-on exercises.

Introduction

When developers say "async and await do not work," the underlying issue is usually not the language feature itself but a mismatch between the mental model and what the code is actually doing. await only pauses the current async function, only waits for a promise-like value, and only helps if the surrounding code is structured to wait for the result.

The Core Rule: await Needs an Async Boundary

await can only be used inside an async function, or at top level in environments that support top-level await. If the current function is not async, the code either fails to parse or the promise is never handled the way you expected.

javascript
1async function loadUser() {
2  const response = await fetch("https://api.example.com/user");
3  return response.json();
4}

The async keyword matters because it tells JavaScript to wrap the function result in a promise and allow suspension at each await.

await Does Not Make Non-Promise Code Asynchronous

Another common problem is awaiting a function that does not return a promise. In that case, await simply unwraps the plain value immediately.

javascript
1function getNumber() {
2  return 42;
3}
4
5async function demo() {
6  const value = await getNumber();
7  console.log(value);
8}

This code works, but it is not waiting for anything meaningful. If the real work happens through callbacks, event listeners, or a library API that does not return a promise, await cannot help until you wrap that API in a promise-aware form.

javascript
1function wait(ms) {
2  return new Promise((resolve) => {
3    setTimeout(resolve, ms);
4  });
5}
6
7async function run() {
8  await wait(1000);
9  console.log("one second later");
10}

Not Awaiting the Async Function Itself

Even if a function contains await, callers still receive a promise. If they ignore that promise, the surrounding control flow continues immediately.

javascript
1async function save() {
2  await new Promise((resolve) => setTimeout(resolve, 500));
3  console.log("saved");
4}
5
6function main() {
7  save();
8  console.log("finished main");
9}
10
11main();

Output:

text
finished main
saved

That order is correct. save() returns a promise, and main does not wait for it. The fix is to either await save() inside an async caller or return the promise and handle it properly.

Array Helpers Often Hide the Problem

forEach is a classic trap. It does not wait for async callbacks.

javascript
1const ids = [1, 2, 3];
2
3ids.forEach(async (id) => {
4  await fetch(`https://api.example.com/items/${id}`);
5  console.log("processed", id);
6});
7
8console.log("loop done");

console.log("loop done") runs before the fetches complete. If you need sequential processing, use a for...of loop. If you need parallel processing, build an array of promises and await Promise.all.

javascript
1async function processAll(ids) {
2  const tasks = ids.map((id) =>
3    fetch(`https://api.example.com/items/${id}`)
4  );
5
6  await Promise.all(tasks);
7  console.log("all requests finished");
8}

Errors Still Need Explicit Handling

await makes async code look synchronous, but rejection still behaves like an exception. If nothing catches it, your code may appear to "stop working" when the real issue is an unhandled rejection.

javascript
1async function loadJson(url) {
2  try {
3    const response = await fetch(url);
4    if (!response.ok) {
5      throw new Error(`HTTP ${response.status}`);
6    }
7    return await response.json();
8  } catch (error) {
9    console.error("request failed:", error);
10    throw error;
11  }
12}

Good error handling is part of making async and await work reliably.

Common Pitfalls

The biggest pitfall is expecting await to block the whole program. It does not. It only pauses the current async function while the event loop keeps running.

Another pitfall is mixing callback-based libraries with await without converting those APIs to promises first. If the function does not return a promise, await has nothing real to wait on.

Developers also misuse array helpers such as forEach, filter, and some with async callbacks. Those methods were not designed to await each iteration, so results often look random until you realize the callbacks are running independently.

Finally, environment support matters. Older runtimes, older browser targets, or misconfigured build tools may not support the syntax you are writing.

Summary

  • 'await only works inside async contexts that support it.'
  • It only waits for promise-like values, not arbitrary callback code.
  • Calling an async function without awaiting it means the caller continues immediately.
  • 'forEach does not wait for async callbacks; use for...of or Promise.all instead.'
  • Treat rejected promises like exceptions and handle them deliberately.

Course illustration
Course illustration

All Rights Reserved.