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