asynchronous programming
node.js
multiple returns
JavaScript
async handling

Handling multiple returns asynchronously 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

In Node.js, an asynchronous function does not "return multiple times" in the same way a synchronous function might emit several intermediate values. Instead, you usually solve the problem by returning one promise that resolves to a combined result, or by using streams, events, or async iterators when values truly arrive over time.

Returning Several Results from One Async Operation

The most common case is simple: you need several values, so return an object or an array from one async function.

javascript
1async function loadUserSummary(userId) {
2  const user = await getUser(userId);
3  const orders = await getOrders(userId);
4
5  return {
6    user,
7    orders,
8    orderCount: orders.length
9  };
10}
11
12loadUserSummary(42).then(result => {
13  console.log(result.user.name);
14  console.log(result.orderCount);
15});

JavaScript functions return one thing, but that one thing can contain many values.

Running Several Async Operations Together

If the values come from independent operations, Promise.all() is usually the right tool.

javascript
1async function loadDashboard(userId) {
2  const [user, orders, notifications] = await Promise.all([
3    getUser(userId),
4    getOrders(userId),
5    getNotifications(userId)
6  ]);
7
8  return { user, orders, notifications };
9}

This runs the requests concurrently rather than one after another. It is a standard pattern for "multiple async returns" because the function still resolves once, but with the combined data.

When Partial Failure Is Acceptable

Promise.all() rejects immediately if any one promise rejects. If you want to collect every result and inspect failures individually, use Promise.allSettled().

javascript
1async function loadOptionalData(userId) {
2  const results = await Promise.allSettled([
3    getUser(userId),
4    getOrders(userId),
5    getRecommendations(userId)
6  ]);
7
8  return results.map(result =>
9    result.status === "fulfilled" ? result.value : null
10  );
11}

That is often the right choice in dashboards and aggregation endpoints where one missing widget should not crash the entire response.

If You Truly Need Many Values Over Time

Sometimes the real requirement is not "multiple return values" but "multiple asynchronous emissions." In that case, a promise is the wrong abstraction because a promise resolves only once.

Use an async generator when values should arrive incrementally:

javascript
1async function* streamPages(fetchPage) {
2  let page = 1;
3
4  while (true) {
5    const items = await fetchPage(page);
6    if (items.length === 0) {
7      return;
8    }
9
10    yield items;
11    page += 1;
12  }
13}
14
15(async () => {
16  for await (const page of streamPages(fetchPage)) {
17    console.log("received page with", page.length, "items");
18  }
19})();

That pattern is a much better fit for paginated APIs, streaming reads, and long-running background workflows.

Legacy Callback Style

Older Node.js code often uses callbacks. A callback can pass multiple result values after the error argument:

javascript
1function loadPair(callback) {
2  setTimeout(() => {
3    callback(null, "first", "second");
4  }, 100);
5}
6
7loadPair((err, a, b) => {
8  if (err) {
9    console.error(err);
10    return;
11  }
12
13  console.log(a, b);
14});

This works, but in modern code promises and async or await are usually clearer and easier to compose.

A Practical Rule

Choose the abstraction based on how many times data should arrive:

  • one final combined result: return a promise that resolves to an object or array
  • several independent async tasks: combine them with Promise.all() or Promise.allSettled()
  • repeated asynchronous emissions: use streams, events, or async generators

Once you frame the problem that way, the "multiple returns" question becomes much easier.

Common Pitfalls

The first pitfall is expecting return inside a callback to return from the outer async function. It only returns from the callback itself.

Another pitfall is running independent awaits sequentially when Promise.all() would be faster. That introduces unnecessary latency.

A third pitfall is using Promise.all() when partial failure should be tolerated. In that case, a single rejected promise can cancel the whole aggregation.

Finally, do not use a promise when you actually need multiple emissions over time. A promise resolves once, so it cannot model a stream of values cleanly.

Summary

  • Async functions return one promise, but that promise can resolve to an object or array containing many values
  • Use Promise.all() for concurrent independent work
  • Use Promise.allSettled() when partial failure is acceptable
  • Use async generators, streams, or events when values arrive over time
  • Pick the abstraction based on whether the result is one combined value or many asynchronous emissions

Course illustration
Course illustration

All Rights Reserved.