Featured successful Node Weekly #624 and Javascript Weekly - 2026-06-02*
Recursion is 1 of those ideas developers study early and spot for years. If the recursive measurement is elemental and the guidelines lawsuit is correct, the codification feels cleanable and safe.
It is elegant for a reason: galore problems are people recursive, and the codification often mirrors really we explicate the logic retired loud. For character walks, nested structures, and divide-and-conquer patterns, recursion tin beryllium easier to publication than definitive loops.
The drawback is beingness limits. Even pinch a correct guidelines lawsuit and sound logic, each recursive telephone still consumes stack space. At immoderate depth, you clang pinch stack overflow.
If you publication Your Debounce Is Lying to You and Your Throttling Is Lying to You, this is the recursion type of the aforesaid pattern: elegant abstraction, hidden operational edge. Even dependency guidance tin dishonesty to you, arsenic explored successful Your Package Manager Is Lying to You. For a different class of silent failure, Your JS Date Is Lying to You covers the parsing, mutation, and timezone traps built into the JavaScript Date API.
Problem Setup: Recursion Hits The Wall
You tin tally everything beneath straight successful a browser console. Let's commencement simple: a recursive sum of each integers from 1 to n.
function sum(n) { if (n === 0) return 0; return n + sum(n - 1); } sum(10);Now push a large input:
sum(100000);What conscionable happened? The usability is logically correct, but each telephone to sum stays connected the stack until the 1 beneath it returns. At extent 100,000 the runtime runs retired of stack abstraction and throws. It has thing to do pinch the consequence being wrong, it is purely a beingness limit connected really galore nested frames the runtime tin clasp astatine once.
The Tail Recursion Rescue Story
The accustomed adjacent measurement is tail telephone optimization. The thought is simple: make the recursive telephone the past point the usability does, truthful the runtime tin reuse the aforesaid framework alternatively of pushing a caller one.
Note that sum is not tail-recursive, moreover though the recursive telephone appears connected the past line. After sum(n - 1) returns, location is still pending work: the consequence must beryllium added to n. A telephone is only successful tail position erstwhile its return worth is forwarded immediately, pinch nary pending computation afterward.
The tail-recursive type moves that pending authorities into an accumulator:
function sumTR(n, acc = 0) { if (n === 0) return acc; return sumTR(n - 1, acc + n); } sumTR(10);Here sumTR(...) is the very past point that happens — nary pending +, nary pending anything. The moving full lives successful acc, not successful waiting stack frames. In theory, a runtime that implements TCO tin execute this successful changeless stack abstraction sloppy of depth.
Now repetition the aforesaid accent input:
sumTR(100000);Even pinch correct tail-recursive structure, galore JavaScript runtimes still allocate a caller stack framework per telephone and propulsion astatine ample depth. This surprises developers who expect TCO to beryllium a cosmopolitan guarantee. ECMAScript 2015 formally specified due tail calls successful strict mode, but astir engines ne'er adopted the characteristic consistently. Some shipped it and past walked it backmost owed to capacity regressions. Others ne'er implemented it astatine all. The consequence is that you cannot presume tail recursion is stack-safe successful accumulation JavaScript, moreover if the codification is correctly system for TCO.
A Note connected Fibonacci
Fibonacci is the go-to recursion textbook illustration and it does tally into stack limits too, but it carries a 2nd problem that makes it moreover worse: exponential clip complexity.
function fib(n) { if (n <= 1) return n; return fib(n - 1) + fib(n - 2); }Each telephone branches into 2 much calls, truthful the full number of calls grows arsenic O(2ⁿ). fib(30) already makes complete a cardinal calls; fib(50) is successful the tens of billions. In a browser this freezes the tab agelong earlier immoderate stack limit is reached, which makes the nonaccomplishment mode look identical to a stack overflow but person a wholly different guidelines cause.
The tail-recursive type of Fibonacci:
function fibTR(n, a = 0, b = 1) { if (n === 0) return a; if (n === 1) return b; return fibTR(n - 1, b, a + b); }This type runs successful linear time, but it still risks stack overflow astatine ample n owed to the aforesaid TCO uncertainty. The exponential type is simply a reddish herring for this chat because it fails for a wholly different reason: stack overflow and exponential blowup are 2 abstracted problems. They look the aforesaid from the extracurricular (the page hangs aliases crashes) but require wholly different fixes.
Runtime Reality (At The Time Of Writing)
At the clip of penning (May 2026), due tail-call optimization support is not thing you tin count connected crossed JavaScript runtimes.
| Chrome | V8 | No | Do not expect stack-safe tail recursion. |
| Node.js | V8 | No | Tail-recursive codification tin still overflow. |
| Deno | V8 | No | Same operational anticipation arsenic Node/Chrome. |
| Firefox | SpiderMonkey | No | Do not dainty tail recursion arsenic a information guarantee. |
| Safari | JavaScriptCore | Inconsistent — JSC has shipped and walked backmost TCO crossed versions | Do not trust connected it; behaviour has varied capable crossed releases that it is not a unchangeable guarantee. |
| Bun | JavaScriptCore-based | Engine-dependent, not a cross-runtime guarantee | Verify connected nonstop version; do not presume cosmopolitan behavior. |
The cardinal constituent is portability. Tail recursion is simply a spot of usability structure, while stack reuse is simply a spot of runtime implementation. Even if 1 motor behaves amended successful 1 version, accumulation JavaScript usually spans aggregate targets, and correctness should not dangle connected optimizer-specific behavior. A usability tin beryllium perfectly tail-recursive successful style and still devour stack per telephone successful the environments your users really run.
Better Patterns for Production Code
Every recursive usability tin beryllium rewritten iteratively, and that is usually the safest prime successful accumulation erstwhile input extent tin grow. Iteration does not trust connected runtime optimizations for stack safety, because it does not devour stack frames per step. This does not mean giving up the recursive intelligence model. You tin still constitute codification that is conceptually recursive but uses an definitive stack aliases a trampoline to negociate power travel without hitting beingness limits.
function sumIter(n) { let acc = 0; for (let one = n; one > 0; i--) acc += i; return acc; } sumIter(1000000);The Trampoline Pattern
If you want to support the recursive building for readability but request to debar stack growth, you tin usage a trampoline: a loop that many times calls a usability that returns either a last consequence aliases different usability to call.
function trampoline(fn) { let consequence = fn; while (typeof consequence === 'function') { consequence = result(); } return result; } function sumTrampoline(n, acc = 0) { if (n === 0) return acc; return () => sumTrampoline(n - 1, acc + n); } trampoline(() => sumTrampoline(100000));Trampolines waste and acquisition stack information for further usability allocations and dispatch overhead, truthful they are astir useful erstwhile preserving recursive building matters much than earthy performance.
This attack scales successful a measurement that does not dangle connected runtime tail-call behavior, which is precisely what you want erstwhile input extent tin grow. If recursive building improves readability for a peculiar problem, these techniques fto you support that intelligence exemplary pinch definitive tradeoffs alternatively of implicit runtime assumptions.
A useful norm of thumb is to support recursion for small, bounded depths that you control, and switch to iterative power travel arsenic soon arsenic extent is user-driven, data-driven, aliases operationally uncertain. For basking paths, benchmark some styles, but do not guidelines correctness connected assumed TCO.
Practical Checklist
- Never presume TCO successful JavaScript for production-critical paths.
- Test pinch realistic precocious bounds, not artifact input sizes.
- Favor iterative implementations erstwhile extent tin grow.
- Treat recursion arsenic a readability tool, not a stack-safety guarantee.
Conclusion
Recursion itself is not the enemy, unverified runtime assumptions are. Tail-recursive style does not automatically make JavaScript stack-safe, and that spread is wherever galore "works connected my machine" surprises travel from successful production.
Use recursion wherever it improves clarity and extent is genuinely bounded. When extent tin turn aliases input is extracurricular your control, for illustration iterative designs that make stack behaviour definitive and portable.
English (US) ·
Indonesian (ID) ·