我无法弄清楚为什么调用recSetTimeOut()
不会导致堆栈溢出错误,而recPromise()
却导致堆栈溢出错误。
const recSetTimeOut = () => {
console.log('in recSetTimeOut');
setTimeout(recSetTimeOut, 0)
};
recSetTimeOut();
const recPromise = () => {
console.log('in recPromise');
Promise.resolve().then(recPromise);
}
recPromise();
为什么会发生?它们之间有什么区别?
你能解释一下幕后的过程吗?
编辑更多信息
在Node.js v12.1.0
和Chrome DevTools
上运行此摘要:
const recSetTimeOut = () => { setTimeout(recSetTimeOut, 0); }
recSetTimeOut();
结果Node
:没有错误。
结果Chrome
:没有错误。
const recPromise = () => { Promise.resolve().then(recPromise); }
recPromise();
结果Node
:
致命错误:无效的表大小分配失败-JavaScript堆内存不足
结果Chrome
:浏览器崩溃。
答案 0 :(得分:5)
让我们依次看看。
const recSetTimeOut = () => {
console.log('in recSetTimeOut');
setTimeout(recSetTimeOut, 0)
};
recSetTimeOut();
这实际上不是递归。您正在向调度程序注册recSetTimeOut
。当浏览器的UI线程空闲时,它将拉出列表中的下一个等待函数,并对其进行调用。调用堆栈永远不会增长。调度程序(本机代码)将始终位于非常短的调用堆栈的顶部。您可以通过发出异常并检查其调用堆栈来验证这一点。
const recPromise = () => {
console.log('in recPromise');
Promise.resolve().then(recPromise);
}
recPromise();
这实际上是一个无限循环,它拒绝将控制权交还给UI。每当承诺解决时,都会立即调用then
处理程序。完成后,将立即调用then
处理程序。完成后... UI线程将饿死,并且UI事件将永远不会被处理。与第一种情况一样,调用堆栈不会增长,因为每个回调都是通过有效的循环进行的。这称为“承诺链”。如果一个承诺解析为一个承诺,则将调用该新的承诺,这不会导致堆栈增长。但是,它 所做的是防止UI线程执行任何操作。
您可以使用console.log((new Error()).stack)
来确认两个堆栈跟踪都基本为空。
这两种解决方案都不会导致堆栈溢出异常,尽管这可能与实现有关;浏览器的调度程序的功能可能与Node的调度程序不同。
答案 1 :(得分:-6)
据我了解,您的问题之所以失败,是因为then
在您调用它时不接受参数。
类似的事情可能会产生预期的结果...
const recPromise = async () => {
return Promise.resolve(recPromise())
}