Question
How can I return the result from a function like foo() that performs an asynchronous operation?
I tried returning the value directly from inside the callback, and I also tried storing the result in a local variable and returning that variable after the asynchronous call. In both cases, the function returns undefined or the variable's initial value instead of the actual response.
Example using jQuery ajax:
function foo() {
var result;
$.ajax({
url: '...',
success: function(response) {
result = response;
// return response;
}
});
return result;
}
Example using Node.js fs.readFile:
function foo() {
var result;
fs.readFile('path/to/file', function(err, data) {
result = data;
// return data;
});
return result;
}
Example using fetch(...).then(...):
function foo() {
var result;
fetch(url).then(function(response) {
result = response;
// return response;
});
return result;
}
Why does this happen, and what is the correct way to work with and return values from asynchronous code in JavaScript?
Short Answer
By the end of this page, you will understand why asynchronous JavaScript functions cannot return their final result immediately, why undefined appears so often in this situation, and how to handle async results correctly using callbacks, Promises, and async/await.
Concept
JavaScript often performs slow tasks asynchronously so the program can continue running while it waits.
Common asynchronous operations include:
- HTTP requests
- Reading files
- Database queries
- Timers
- User interactions
The key idea is this:
- A synchronous function finishes its work before returning.
- An asynchronous function starts work now, but finishes later.
That is why this pattern does not work:
function foo() {
let result;
fetch('/api/data').then(function(response) {
result = response;
});
return result;
}
When return result runs, the fetch request has not finished yet. The callback inside .then(...) will run later, after the current function has already returned.
Returning inside the callback also does not return from foo():
fetch('/api/data').then(function() {
response;
});
Mental Model
Imagine ordering food at a restaurant.
A synchronous function is like walking to a vending machine:
- you press a button
- the item comes out immediately
- you leave with the item
An asynchronous function is like ordering food from a kitchen:
- you place the order
- the kitchen starts preparing it
- you do not get the meal instantly
- you get it later when it is ready
Your foo() function is trying to do this:
- Place the order
- Immediately leave with the food
That cannot work because the food is still being prepared.
Instead, you must either:
- give the restaurant a way to notify you later (callback)
- receive a claim ticket for the future meal (Promise)
- wait for the meal in a cleaner way (
await)
That is the core mental model: async code gives you the result later, not now.
Syntax and Examples
1. Using a callback
A callback is a function you pass in so it can be called when the async work is done.
function foo(callback) {
fs.readFile('path/to/file', 'utf8', function(err, data) {
if (err) {
callback(err, null);
return;
}
callback(null, data);
});
}
foo(function(err, result) {
if (err) {
console.error(err);
return;
}
console.log(result);
});
Here, foo does not return the file contents directly. Instead, it passes the result to callback when ready.
2. Returning a Promise
A Promise represents a value that will be available in the future.
function foo() {
return ()
.(() {
response.();
});
}
().(() {
.(result);
});
Step by Step Execution
Consider this code:
function foo() {
let result;
fetch('/api/data').then(function(response) {
result = response;
console.log('Inside then:', result);
});
console.log('Before return:', result);
return result;
}
const value = foo();
console.log('After calling foo:', value);
What happens step by step
foo()starts running.resultis declared but has the valueundefined.fetch('/api/data')starts the network request..then(...)registers a callback to run later when the request finishes.- JavaScript does not wait here.
console.log('Before return:', result)runs and printsundefined.
Real World Use Cases
Asynchronous results are used everywhere in JavaScript.
API calls
Frontend apps fetch data from servers:
async function loadUsers() {
const response = await fetch('/api/users');
return response.json();
}
Reading files in Node.js
Servers read configuration files, templates, or uploaded data:
const fs = require('fs');
fs.readFile('config.json', 'utf8', function(err, text) {
if (err) {
console.error(err);
return;
}
console.log(text);
});
Database queries
Applications wait for the database before continuing:
async function getUserById() {
db..(id);
}
Real Codebase Usage
In real projects, developers rarely try to force async code into synchronous style. Instead, they structure functions around async flow.
Common pattern: return a Promise
function getPosts() {
return fetch('/api/posts').then(function(response) {
if (!response.ok) {
throw new Error('Failed to load posts');
}
return response.json();
});
}
This lets callers decide how to handle the result:
getPosts()
.then(function(posts) {
console.log(posts);
})
.catch(function(error) {
console.error(error);
});
Common pattern: async function with validation
Common Mistakes
1. Returning from inside the callback
Broken code:
function foo() {
fs.readFile('file.txt', 'utf8', function(err, data) {
return data;
});
}
Why it is wrong:
return datareturns from the callback function only- it does not return from
foo()
2. Returning a variable too early
Broken code:
function foo() {
let result;
setTimeout(function() {
result = 42;
}, 1000);
return result;
}
Why it is wrong:
return resultruns beforesetTimeoutfires
3. Forgetting to return the Promise
Broken code:
Comparisons
| Approach | How it works | Good for | Downsides |
|---|---|---|---|
| Callback | Pass a function to be called later | Older APIs, simple event-based work | Can become nested and hard to read |
| Promise | Return an object representing a future value | Chaining async operations | Requires understanding .then() and .catch() |
async/await | Write Promise-based code in a synchronous-looking style | Modern JavaScript application code | Still asynchronous; must be used with Promises |
Returning a value vs returning a Promise
| Pattern | What is returned |
|---|
Cheat Sheet
Core rule
You cannot return the final value from async code synchronously.
Wrong pattern
function foo() {
let result;
asyncOperation(function(data) {
result = data;
});
return result;
}
Correct patterns
Callback
function foo(callback) {
asyncOperation(function(data) {
callback(data);
});
}
Promise
function foo() {
return asyncOperationPromise();
}
Async/await
async function foo() {
const data = await ();
data;
}
FAQ
Why does my function return undefined after fetch?
Because fetch is asynchronous. Your function returns before the request finishes.
Can I force async code to return synchronously?
Usually no. The correct solution is to use callbacks, Promises, or async/await.
Does return inside .then() return from the outer function?
No. It returns from the .then() callback and affects the Promise chain, not the outer synchronous function.
What does an async function return?
It always returns a Promise.
Should I use callbacks or async/await?
For modern JavaScript, prefer Promises and async/await. Use callbacks mainly when working with older APIs.
Do I still need error handling with await?
Yes. Use try/catch or let the error propagate to the caller.
Mini Project
Description
Build a small JavaScript utility that loads JSON data from an API and prints selected information. This project demonstrates the correct way to return asynchronous results using Promises and async/await instead of trying to return values too early.
Goal
Create a function that fetches user data asynchronously and returns parsed JSON so another function can use it safely.
Requirements
- Create an async function that requests data from a URL.
- Check whether the HTTP response is successful.
- Parse the response as JSON.
- Return the parsed data from the async function.
- Call the function and print part of the result.
- Handle errors clearly.
Keep learning
Related questions
Deep Cloning Objects in JavaScript: Methods, Trade-offs, and Best Practices
Learn how to deep clone objects in JavaScript, compare structuredClone, JSON methods, and recursive approaches with examples.
Get Screen, Page, and Browser Window Size in JavaScript
Learn how to get screen size, viewport size, page size, and scroll position in JavaScript across major browsers with clear examples.
How JavaScript Closures Work: A Beginner-Friendly Guide
Learn how JavaScript closures work with simple explanations, examples, common mistakes, and practical use cases for real code.