Node.js
callback function
return value
asynchronous programming
JavaScript

return value of a callback function in Node.js

Master System Design with Codemia

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

Introduction

A return value inside a Node.js callback returns to the callback caller, not to the outer asynchronous API that scheduled the callback. That is why beginners often write return value inside a callback and then wonder why the outer function still returns undefined. The fix is to understand where the asynchronous boundary is and choose the right way to pass results across it.

Why return Inside A Callback Does Not Escape The Outer Function

Consider this common mistake:

javascript
1const fs = require("fs");
2
3function readConfig() {
4  fs.readFile("config.json", "utf8", (err, data) => {
5    if (err) {
6      return null;
7    }
8    return data;
9  });
10}
11
12console.log(readConfig());

readConfig() returns before readFile finishes. The return data only returns from the callback function itself. It does not teleport the value back through the already-finished outer function.

That is the core rule: asynchronous callbacks happen later, so their return values are not the outer function’s return values.

The Callback Is Where The Result Must Be Handled

With callback-style APIs, the standard approach is to pass another callback and use it when the async operation completes.

javascript
1const fs = require("fs");
2
3function readConfig(done) {
4  fs.readFile("config.json", "utf8", (err, data) => {
5    if (err) {
6      done(err);
7      return;
8    }
9
10    done(null, data);
11  });
12}
13
14readConfig((err, data) => {
15  if (err) {
16    console.error(err);
17    return;
18  }
19
20  console.log(data);
21});

This is the normal error-first callback pattern in Node.js.

Returning From The Callback Still Has A Meaning

A return inside the callback is not useless. It can still be helpful for control flow inside the callback itself.

javascript
1fs.readFile("config.json", "utf8", (err, data) => {
2  if (err) {
3    console.error(err);
4    return;
5  }
6
7  console.log(data);
8});

Here return stops the callback body after error handling. It just does not return a value to readFile() or to the outer function that originally called readFile().

Promises Are Usually Clearer In Modern Code

If you want the outer function to produce a value asynchronously, return a Promise instead.

javascript
1const fs = require("fs/promises");
2
3async function readConfig() {
4  const data = await fs.readFile("config.json", "utf8");
5  return data;
6}
7
8readConfig()
9  .then(data => console.log(data))
10  .catch(err => console.error(err));

Now the outer function really does return something meaningful: a promise that eventually resolves to the data.

Comparing The Two Models

Callback model:

  • the result is passed to a callback later,
  • 'return inside the callback only affects that callback.'

Promise model:

  • the outer function can return a promise,
  • 'return inside an async function resolves that promise.'

This is why many modern Node.js codebases prefer promises or async and await for new code.

A Good Mental Model

Think of a callback as a function handed to someone else for later execution. When it finally runs, its return value goes back only to whoever invoked that callback function at that moment. It does not retroactively change what an earlier caller already received.

Once you internalize that timing model, the confusion around callback return values mostly disappears.

Common Pitfalls

  • Expecting return value inside a callback to become the outer function’s result.
  • Mixing synchronous-looking return logic with callback-based asynchronous code.
  • Forgetting to handle errors in the callback before using the result.
  • Converting callback code to promises partially, creating two inconsistent result paths.
  • Using callbacks when promise-based APIs would make the data flow much clearer.

Summary

  • A callback’s return value returns only from the callback itself.
  • It does not become the return value of the outer asynchronous function.
  • In callback style, pass results through the callback parameters.
  • In modern Node.js, promises and async and await often express result flow more clearly.
  • Understanding the timing boundary is more important than the return keyword itself.

Course illustration
Course illustration