我在想,当异步函数递归地无限调用自己时会发生什么。我的想法是,它不会导致堆栈溢出。但是我无法确切指出为什么会这样。
const foo = async () => {
const txt = await Promise.resolve("foo");
console.log(txt);
foo();
}
foo();
上面的代码无限打印“ foo”,而不会溢出堆栈。
我的想法是该代码在概念上类似于以下代码,它不会引起堆栈溢出,因为对foo()
的递归调用位于回调内部,对foo()
的原始调用将在返回之前返回那个。
const bar = () => {
console.log("foo");
foo();
}
const foo = () => {
setImmediate(bar);
}
foo();
我正在寻找有关异步功能情况的确切答案。
答案 0 :(得分: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();
案例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();
答案 1 :(得分:2)
此功能是
的语法糖const foo = () =>
Promise.resolve(
Promise.resolve("foo")
.then(txt => {
console.log(txt);
foo();
})
);
foo();
此本身可以用更少的依赖项重写为
const foo = () =>
queueMicrotask(() =>
queueMicrotask(() => {
console.log("foo");
foo();
})
);
foo();
Window.queueMicrotask
是一种非常新的方法,它为我们提供了一种触发queue a microtask操作的方法,该操作由Promise.resolve触发,因此await
也会触发。
基本上,此操作在当前执行结束时但在当前事件循环结束之前推送微任务。
该算法的第六点是
将设置任务的脚本评估环境设置对象设置为空集。
这就是为什么您在这里没有堆栈溢出的原因。但是,由于您永远不会退出事件循环,因此您正在阻止浏览器。