How to convert sync and async recursive function to iteration in JavaScript
Data Structures & Algorithms practice on Codemia
Step through 300 algorithm problems with animated visualisers that show the data structure changing as the code runs.
Introduction
Converting recursion to iteration in JavaScript usually means replacing the call stack with your own explicit data structure, most often an array used as a stack. The idea is the same for synchronous and asynchronous code, but asynchronous recursion needs one extra design decision: whether the iterative version should stay sequential or allow concurrency.
This is why a direct mechanical rewrite often fails. Recursion hides state inside call frames, while iteration requires you to store that state yourself.
How Recursive State Becomes Iterative State
In recursion, each call frame remembers:
- where it is in the algorithm
- local variables
- what still has to happen after child calls return
An iterative rewrite has to store that information explicitly.
The simplest case is depth-first traversal of a tree or graph.
Converting Synchronous Recursion to Iteration
Suppose you have a recursive depth-first traversal:
The iterative version uses an explicit stack:
The reverse loop preserves the same left-to-right traversal order that the recursive version had.
That is the core transformation:
- recursive call becomes
stack.push(...) - returning from recursion becomes the next loop iteration
Another Simple Example: Factorial
Not every recursive function needs an explicit stack. Some only need accumulated state.
Recursive:
Iterative:
Here the recursive state collapses into a loop counter and an accumulator, so a custom stack is not necessary.
Converting Async Recursion to Iteration
Async recursion has the same structural issue plus await. Consider a recursive function that processes tree nodes one at a time:
If you want the iterative version to preserve the same sequential behavior, use a loop and stack with await inside the loop:
This still processes nodes sequentially, just without recursive call frames.
Async Iteration With Explicit Queues
Sometimes recursion really models a queue rather than a stack. Breadth-first logic is often clearer with an explicit queue:
This is still iterative, but it preserves breadth-first rather than depth-first behavior.
When Async Iteration Can Be Concurrent
An async recursive function often uses await in a sequential way. When converting to iteration, you must decide whether to preserve that or intentionally introduce concurrency.
Sequential:
Concurrent:
These are not equivalent. The second version changes execution order, timing, and resource usage. So an async recursive rewrite is not just a syntax change. It is also a concurrency decision.
A General Recipe
When converting recursion to iteration:
- Identify the recursive state.
- Decide whether the traversal is stack-like or queue-like.
- Store the pending work explicitly.
- Preserve ordering deliberately.
- For async logic, decide whether the iterative version should stay sequential.
That recipe works for many tree, graph, parser, and filesystem-walking problems.
Common Pitfalls
One common mistake is converting recursion to a loop but forgetting the hidden post-call work. If the recursive function does more after child calls return, you may need a richer stack frame object, not just the node itself.
Another mistake is accidentally changing traversal order. A depth-first recursive function can become a different algorithm if you push children in the wrong order.
Async rewrites often fail by introducing unintended concurrency. Replacing recursive await logic with Promise.all may be faster, but it also changes behavior and can overload external services.
Finally, iteration avoids call stack overflow, but it does not automatically simplify the algorithm. Some recursive problems become clearer iteratively, while others become more verbose because you now manage all state manually.
Summary
- Recursive state must be stored explicitly in an iterative rewrite.
- Simple numeric recursion may become a loop with accumulators.
- Tree and graph recursion often becomes an explicit stack or queue.
- Async recursion can be rewritten iteratively with
awaitinside the loop to preserve sequential behavior. - Be careful not to change traversal order or concurrency semantics by accident.
Related reading
- How to count each digit in a range of integers?
- How to count groups of same cells in a 2d array?
- How to count integers between large A and B with a certain property?
- How to count Multiply-Adds operations?
- How to convert this Parallel.ForEach code to async/await
- How to copy parameters from global model to thread-specific model
- How to copy/move all objects in Amazon S3 from one prefix to other using the AWS SDK for Node.js
- How to create a checkbox with a clickable label?

DSA Fundamentals
Master algorithmic patterns and data structures through hands-on LeetCode-style problems - from arrays and hashing to dynamic programming and advanced graphs.
View the courseTrack what you have practised
A free account saves your progress, solutions and study plan across every problem on Codemia.
Data Structures & Algorithms practice on Codemia
Step through 300 algorithm problems with animated visualisers that show the data structure changing as the code runs.