最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

javascript - Why doesn't an infinitely recursive async function cause stack overflow? - Stack Overflow

programmeradmin1浏览0评论

I was thinking what happens when an async function recursively calls itself infinitely. My thought was that it will not cause stack overflow. But I can't exactly point out why this is the case.

const foo = async () => {
    const txt = await Promise.resolve("foo");
    console.log(txt);
    foo();
}

foo();

The code above prints "foo" infinitely without overflowing the stack.

My idea is that the code is conceptually similar to the following, it doesn't cause stack overflow because the recursive call to foo() is inside the callback, the original call to foo() will return before that.

const bar = () => {
    console.log("foo");
    foo();
}

const foo = () => {
    setImmediate(bar);
}

foo();

I am looking for the exact answer as to what happens in the case of the async function.

I was thinking what happens when an async function recursively calls itself infinitely. My thought was that it will not cause stack overflow. But I can't exactly point out why this is the case.

const foo = async () => {
    const txt = await Promise.resolve("foo");
    console.log(txt);
    foo();
}

foo();

The code above prints "foo" infinitely without overflowing the stack.

My idea is that the code is conceptually similar to the following, it doesn't cause stack overflow because the recursive call to foo() is inside the callback, the original call to foo() will return before that.

const bar = () => {
    console.log("foo");
    foo();
}

const foo = () => {
    setImmediate(bar);
}

foo();

I am looking for the exact answer as to what happens in the case of the async function.

Share Improve this question edited May 19, 2019 at 6:40 Raju Ahmed asked May 19, 2019 at 6:23 Raju AhmedRaju Ahmed 1807 bronze badges 2
  • 5 It's because the recursive async call will wait for the current execution to finish before making a new stack frame. Hence you don't run out of stack space. stackoverflow./questions/39459236/… – VLAZ Commented May 19, 2019 at 6:44
  • Yes, your explanation is correct. You would probably have a memory leak if you would await foo() though. – Bergi Commented May 19, 2019 at 14:42
Add a ment  | 

2 Answers 2

Reset to default 5

This function is syntactic sugar for

const foo = () => 
  Promise.resolve(
    Promise.resolve("foo")
    .then(txt => {
      console.log(txt);
      foo();
    })
  );

foo();

This itself can be rewritten with less dependencies as

const foo = () =>
  queueMicrotask(() =>
    queueMicrotask(() => {
      console.log("foo");
      foo();
    })
  );
foo();

Window.queueMicrotask is a quite new method that gives us a way to trigger the queue a microtask operation that Promise.resolve and hence await do trigger.
Basically, this operation pushes the microtask at the end of the current execution, but before the end of the current event loop.

The sixth point of the algorithm reads

Set task's script evaluation environment settings object set to an empty set.

This is why you don't have a stack-overflow here. However, since you are never exiting the event loop, you are blocking the browser.

Your code doesn't produce stack overflow because when you have call foo inside function it is not awaited. If you write await foo(); then it should cause stack overflow.

Consider below two cases:

Case 1 Here as per your code. From a() it will call foo without await. So what will happen when it calls foo() as it is async function, it would be scheduled to run after the current execution resolves. Or even more precisely, it will be queued for later execution and immediately a() will also continue from next line. You can see the output that a() is getting ended first, it doesn't wait for call stack for foo to return;

const foo = async () => {
    const txt = await Promise.resolve("foo");
    console.log(txt);
}

const a = async () => {
    const txt = await Promise.resolve("a");
    console.log(txt);
    foo();
    console.log("-- ENd of a() --");
}

a();

Case 2 Here inside a() it will call foo with await. You can see the output that a() is waiting for return from foo() then only it will continue on next line.

const foo = async () => {
    const txt = await Promise.resolve("foo");
    console.log(txt);
}

const a = async () => {
    const txt = await Promise.resolve("a");
    console.log(txt);
    await foo();
    console.log("-- ENd of a() --");
}

a();

发布评论

评论列表(0)

  1. 暂无评论