javascript - 递归承诺会导致堆栈溢出?
问题描述
例如,我发现了一些基于 Promise 的 api 库,我需要在某个时间间隔、无限次(如通常的后端循环)中使用该库发出 api 请求。这个 api 请求 - 实际上是一系列的承诺。
所以,如果我写这样的函数:
function r(){
return api
.call(api.anotherCall)
.then(api.anotherCall)
.then(api.anotherCall)
...
.then(r)
}
会不会导致栈溢出?
我想出的解决方案是使用 setTimeout 进行r
递归调用。
function r(){
return api
.call(api.anotherCall)
.then(api.anotherCall)
.then(api.anotherCall)
.then(()=>{setTimeout(r, 0)})
}
所以 setTimeoutr
只有在调用堆栈为空时才会调用。
这是一个好的解决方案,还是有一些递归调用承诺的标准方法?
解决方案
这会导致stackoverflow吗?
不,它不会。根据 Promise 规范,.then()
等待堆栈完全展开,然后在堆栈清除后调用(基本上是在事件循环的下一个滴答声中)。因此,.then()
在当前事件完成处理并且堆栈展开后,已经异步调用。您不必使用setTimeout()
来避免堆栈堆积。
无论您重复多少次,您的第一个代码示例都不会有任何堆栈堆积或堆栈溢出。
在Promises/A+ 规范中,第 2.2.4 节这样说:
2.2.4 在执行上下文堆栈只包含平台代码之前,不得调用 onFulfilled 或 onRejected。[3.1]。
并且,“平台代码”在 3.1 中定义:
“平台代码”是指引擎、环境和承诺实现代码。在实践中,此要求确保 onFulfilled 和 onRejected 在调用 then 的事件循环之后异步执行,并使用新堆栈。这可以通过 setTimeout 或 setImmediate 等“宏任务”机制实现,也可以通过 MutationObserver 或 process.nextTick 等“微任务”机制实现。由于 Promise 实现被认为是平台代码,它本身可能包含一个任务调度队列或调用处理程序的“蹦床”。
ES6 Promise 规范使用不同的词,但产生相同的效果。在 ES6 中,promise.then()
是通过将作业加入队列然后让该作业得到处理来执行的,并且只有在没有其他代码运行并且堆栈为空时才处理该作业。
这就是ES6 规范中描述的诸如作业之类的运行方式:
Job 是一个抽象操作,当当前没有其他 ECMAScript 计算正在进行时,它会启动 ECMAScript 计算。作业抽象操作可以定义为接受任意一组作业参数。
只有在没有正在运行的执行上下文且执行上下文堆栈为空时,才能启动 Job 的执行。PendingJob 是对未来执行 Job 的请求。PendingJob 是一个内部记录,其字段在表 25 中指定。一旦启动 Job 的执行,Job 总是执行到完成。在当前运行的作业完成之前,不得启动其他作业。但是,当前运行的 Job 或外部事件可能会导致额外的 PendingJobs 排队,这些 PendingJobs 可能会在当前运行的 Job 完成后的某个时间启动。
推荐阅读
- javascript - 可以将 Promise 的返回用作 Promise.all() 执行中下一个函数调用的输入吗?
- rust-tokio - 为什么不能将 tokio::task::spawn 重构为 fn?
- python - skimage 的意思是没有返回预期的结果?
- javascript - 在下拉菜单中选择项目时如何显示内容?
- javascript - 使用 MySQL 数据更新 ChartJS
- swift - 为什么我收到一个奇怪的错误:nw_protocol_get_quic_image_block_invoke dlopen libquic 在 SwiftUI 中失败?
- c - 如何排除正在运行的可执行文件 C
- php - Android URL 连接错误:使用 XAMPP 的数据库连接错误
- java - Docker 中的 Java RMI
- reactjs - React 和 Redux:管理 Redux 自定义中间件列表