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 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.
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().
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:
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:
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()orPromise.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

