Search code examples
javascriptrecursionpromiseasync-awaitstack-overflow

Why recursive async functions in Javascript lead to stack overflow?


Consider this snippet:

function f() {
  return new Promise((resolve, reject) => {
    f().then(() => {
      resolve();
    });
  });
}

f();

which can also be written like this:

async function f() {
  return await f();
}

f();

If you run any of the given two codes, you'll face this error:

(node:23197) UnhandledPromiseRejectionWarning: RangeError: Maximum call stack size exceeded

My question is why? Before answering my question, please consider my argument:

I understand the concept of recursion and how it leads to a stack overflow if there's no stopping condition. But my argument here is that once the first f(); is executed, it will return a Promise and it exits the stack so this recursion should not face any stack overflow ever. To me, this should behave the same as:

while (1) {}

Of course, if I write it like this, it will be fixed:

function f() {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      f().then(() => {
        resolve();
      });
    }, 0);
  });
}

f();

which is a different story and I have no problem with it.

[UPDATE]

My bad, I forgot to mention I was testing with node v8.10.0 on the server side.


Solution

  • Why would you not expect it to cause infinite recursion? The constructor of the promise is calling f recursively so the promise never gets to construct as an infinite recursion loop occurs before the promise is constructed.

    https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise

    From the link above

    The executor function is executed immediately by the Promise implementation, passing resolve and reject functions (the executor is called before the Promise constructor even returns the created object).