JavaScript Async/Await — Spot the Bug!
This is a daily Javascript challenge from the CodeShot archive. Practice your knowledge of Async Return Inside Then Bug and improve your technical interview readiness.
async function getData(url) {
fetch(url)
.then(res => res.json())
.then(data => {
return data
})
}
Detailed Explanation
Why This Question Matters
If you've been coding in JavaScript for a while, you've probably seen this bug. It’s one of those "invisible" errors that doesn't throw a red screen or crash your app immediately, but instead leaves you staring at undefined in your console while wondering why your API call isn't working.
The core of the problem is a fundamental misunderstanding of how async/await interacts with Promises. Many developers treat the async keyword like a magic switch that automatically makes everything inside the function wait, or they mix .then() chains with async functions without realizing they are two different ways of doing the same thing.
When you get this wrong, you aren't just writing inefficient code—you're creating race conditions and bugs that are a nightmare to debug in production.
Understanding the Code
Let's look at the snippet again:
At first glance, it looks fine. You're calling fetch, converting the response to JSON, and returning the data. But here is the catch: the function isn't actually returning anything.
Here is what's happening internally:
1. You call getData(url). Because it's marked async, it immediately returns a Promise.
2. Inside the function, fetch(url) starts an asynchronous operation.
3. The .then() chain is set up to handle the result *whenever that operation finishes*.
4. However, the getData function itself finishes executing its synchronous block almost instantly. Since there is no return statement at the top level of the function, it returns undefined (wrapped in a Promise).
The return data inside the .then() block is returning a value to the *Promise chain*, not to the getData function. It's like sending a letter to a PO Box and expecting the post office to suddenly teleport the contents into your hand.
Finding the Correct Answer
To fix this, you have two real options. You either need to return the entire Promise chain or use await.
Option A: Returning the Promise chain
By adding
return before fetch, the function now returns the Promise created by the fetch chain. The caller can then .then() that result.
Option B: Using await (The Modern Way)
This is the correct approach for an
async function. await pauses the execution of the function until the Promise resolves. The return data now actually refers to the function's output.
The original code failed because it mixed the "old" .then() syntax with the "new" async keyword without actually linking the results together.
Common Mistakes Developers Make
The biggest mistake is assuming async makes the function "wait" for any Promise inside it. It doesn't. async just ensures the function returns a Promise. You have to explicitly tell JavaScript where to pause using await.
Another common trip-up is forgetting that res.json() is *also* an asynchronous operation. I've seen plenty of senior devs write:
const data = await fetch(url).json();
This will throw an error because .json() is a method on the response object, and you have to await the result of that method call specifically.
Lastly, there's the "Silent Failure." If you forget the return or await, JavaScript won't scream at you. It will just give you undefined. This is why you should always use TypeScript or strict linting—it helps catch these "missing return" issues before they hit your staging environment.
Real-World Usage
In a production environment, you'll rarely see a fetch call this naked. You'll usually have error handling and timeouts. When you move to async/await, you get the benefit of using try/catch blocks, which are far more readable than .catch() chains.
Here is how this looks in a real-world service module:
Using await makes the code read linearly, which is much easier for a teammate to review during a PR than a nested tree of .then() callbacks.
Key Takeaways
- async doesn't mean "wait." It means "this function returns a Promise."
- Return your Promises. If you use .then(), you must return the chain. If you use async, you must await the result before returning it.
- .json() is async. Don't forget to await the parsing of the response body.
- Prefer async/await over .then(). It's cleaner, easier to wrap in try/catch, and looks more like the synchronous code we're used to writing.
Why this matters
Understanding Async Return Inside Then Bug is crucial for passing technical interviews. In real-world applications, this concept often leads to subtle bugs if not handled correctly. For more details, you can always refer to the official MDN Documentation.