Categories


Archives


Recent Posts


Categories


Async/Await and Recursion

astorm

Frustrated by Magento? Then you’ll love Commerce Bug, the must have debugging extension for anyone using Magento. Whether you’re just starting out or you’re a seasoned pro, Commerce Bug will save you and your team hours everyday. Grab a copy and start working with Magento instead of against it.

Updated for Magento 2! No Frills Magento Layout is the only Magento front end book you'll ever need. Get your copy today!

So here’s a “fun” side effect of async/await in NodeJS.

I had a bug. It seemed like it might be run away recursion bug.

NodeJS doesn’t let you set an explicit recursion depth limit, but it does limit the size of your call stack. So how could there be a recursion bug without Node throwing up errors and/or crashing my program?

const recurse = async () => {
    // ... function does some work ...
    await someOtherFunction()
    // ... function does some more work after returning from await ...
    if(/* conditional with an "always true" bug */) {
        recurse()
    }
}

When you await in NodeJS, your function stops executing but NodeJS continues to execute the rest of the program outside your function. In the above example, execution of the recurse function will continue when the promise returned by someOtherFunction finally resolves. (If you didn’t follow along with that you might be interested in this series on async javascript programming).

The problem is this: From a NodeJS internals point of view your function doesn’t really resume. Behind the scenes the code after await gets wrapped up in a promise (or something promise like?) and executed asynchronously.

There’s lots of consequences to this — but for our purposes the big one is the original call stack is gone.

Since the call stack is empty after await, this means the call stack doesn’t grow with each recursive call. This means your call stack never hits a size limit and the recursion continues as long as it can.

The extra confounding bit is, depending on the work being done by this the function, this may or may not, cause an obvious performance issue in your program. In our case this was a function called every time we handled an incoming HTTP request, and a function that scheduled a bunch of additional work on the event loop. After about 300 or so request (with 300 recursive functions always running, always scheduling more event loop work), NodeJS just stopped.

Copyright © Alan Storm 1975 – 2020 All Rights Reserved

Originally Posted: 1st June 2020