我不明白为什么打电话recSetTimeOut()
不会导致堆栈溢出错误,而recPromise()
does.
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
and Chrome DevTools
:
const recSetTimeOut = () => { setTimeout(recSetTimeOut, 0); }
recSetTimeOut();
Result Node
: 没有错误。
Result Chrome
: 没有错误。
const recPromise = () => { Promise.resolve().then(recPromise); }
recPromise();
Result Node
:
致命错误:表大小无效分配失败 - JavaScript 堆内存不足
Result Chrome
: 浏览器崩溃。
让我们依次看一下。
const recSetTimeOut = () => {
console.log('in recSetTimeOut');
setTimeout(recSetTimeOut, 0)
};
recSetTimeOut();
这实际上不是递归。您正在注册recSetTimeOut
与调度程序。当浏览器的 UI 线程空闲时,它将从列表中拉出下一个等待函数,并调用它。调用堆栈永远不会增长;调度程序(本机代码)将始终位于非常短的调用堆栈的顶部。您可以通过发出异常并检查其调用堆栈来验证这一点。
- 这个函数实际上并不是递归的;堆栈不会增长。
- 每次调用后,它将控制权交还给 UI 线程,从而允许处理 UI 事件。
- 仅当 UI 完成其操作并调用下一个计划任务时,才会发生下一次调用。
const recPromise = () => {
console.log('in recPromise');
Promise.resolve().then(recPromise);
}
recPromise();
这实际上是一个无限循环,拒绝将控制权交还给 UI。每次承诺兑现时,then
处理程序立即被调用。完成后,会出现一个then
处理程序立即被调用。完成后... UI 线程将饥饿,并且 UI 事件将永远不会被处理。与第一种情况一样,调用堆栈不会增长,因为每个回调实际上都是由循环进行的。这称为“承诺链”。如果一个 Promise 解析为一个 Promise,则会调用该新 Promise,并且这不会导致堆栈增长。什么does不过,do 是阻止 UI 线程执行任何操作。
- 实际上是无限循环。
- 拒绝将控制权交还给 UI。
- 调用堆栈不会增长。
- 下一个调用会立即调用,并且具有极大的偏见。
您可以使用以下命令确认两个堆栈跟踪实际上都是空的console.log((new Error()).stack)
.
这两种解决方案都不应导致堆栈溢出异常,尽管这可能取决于实现;浏览器的调度程序的功能可能与 Node 的不同。
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)