为什么无限递归异步函数不会导致堆栈溢出?

Raj*_*med 10 javascript async-await

我在想,当异步函数递归调用自己无限时会发生什么。我的想法是,它不会导致堆栈溢出。但是我无法确切指出为什么会这样。

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

foo();

Run Code Online (Sandbox Code Playgroud)

上面的代码无限打印“ foo”,而不会溢出堆栈。

我的想法是,代码在概念上与以下代码类似,它不会导致堆栈溢出,因为递归调用foo()位于回调内部,而对的原始调用foo()将在此之前返回。

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

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

foo();
Run Code Online (Sandbox Code Playgroud)

我正在寻找有关异步功能情况下的确切答案。

Kai*_*ido 5

这个函数是语法糖

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

foo();
Run Code Online (Sandbox Code Playgroud)

这本身可以用更少的依赖性重写为

const foo = () =>
  queueMicrotask(() =>
    queueMicrotask(() => {
      console.log("foo");
      foo();
    })
  );
foo();
Run Code Online (Sandbox Code Playgroud)

Window.queueMicrotask是一种相当新的方法,它为我们提供了一种触发队列微任务操作的方法,该操作由 Promise.resolve 触发,从而await触发。
基本上,此操作在当前执行结束时、当前事件循环结束之前推送微任务。

算法的第六点是

将任务的脚本评估环境设置对象集设置为空集。

这就是为什么这里没有堆栈溢出的原因。但是,由于您永远不会退出事件循环,因此您会阻塞浏览器。


Kar*_*ran 2

您的代码不会产生堆栈溢出,因为当您在foo函数内部进行调用时,它不会被await编辑。如果你这样写await foo();,那么它应该会导致堆栈溢出。

考虑以下两种情况:

情况 1 这里根据您的代码。从a()它会调用foo没有await. foo()那么当它作为函数调用时会发生什么async,它也会would be scheduled to run after the current execution resolves. Or even more precisely, it will be queued for later execution立即a()从下一行继续。您可以看到首先结束的输出a(),它不等待调用堆栈foo返回;

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();
Run Code Online (Sandbox Code Playgroud)

案例 2 这里a()它会foo调用await. a()您可以看到正在等待返回的输出,foo()只有它会在下一行继续。

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();
Run Code Online (Sandbox Code Playgroud)