kotlin - 递归调用 Kotlin 挂起函数是否安全?
问题描述
我有一个 API 可能会响应“稍后再试”。在这种情况下,我想递归调用该函数(以自动传播取消)。
简化示例:
suspend fun loadData() {
runCatching { someApi.loadData() }
.onSuccess { response ->
if (response is Response.TryAgain) {
delay(1000)
loadData()
}
}
}
如果我理解正确,则此代码不应导致StackOverflowError
(与常规的非挂起递归函数相反)。
我从Roman Elizarov 的文章中得到了这个想法,但我在这里没有使用 a DeepRecursiveFunction
。
我为Android编写了一些测试,似乎:
suspend
没有悬挂点的函数在 1000-4000 左右的深度抛出StackOverflowError
(就像非悬挂点一样)suspend
带有悬挂点(yield()
或delay()
)的函数允许达到 1-2 百万的深度,其中OutOfMemoryError
发生
这种行为是否记录在某个地方,或者它只是一个将来可能会改变的实现细节?我在coroutines design document或coroutines guide以及这里的 SO中都没有找到答案。
总的来说,如果调用深度远低于 100 万,使用这种模式是否是个好主意?有更好的可取消解决方案吗?
解决方案
如果tailrec优化不适用于您的情况,您可以手动将递归函数转换为 while-loop:
suspend fun loadData() {
var getResponse = false
while (!getResponse) {
runCatching { someApi.loadData() }
.onSuccess { response ->
if (response is Response.TryAgain) {
delay(1000)
} else {
getResponse = true
}
}
}
}
推荐阅读
- c++ - 如何使用 OpenCV 解决图像处理相机 IO 延迟
- php - 用php分解和重新匹配数据
- java - 为什么在我给定的java作业中需要线程?
- c# - 如何使用不同的单选按钮更改 tabcontrol 索引?C#
- c# - 在另一个 DataGridView 中显示一个 DataGridView 的已编辑行
- c++ - GCC:当层次结构中有虚拟继承时,C++11 内联对象初始化(使用“this”)不起作用
- angular - 尝试移植 ng-bootstrap 示例,但 ngbDropdownToggle 工作
- java - 通过 Gradle 的 Spring Boot 自定义启动器
- mysql - 如何编写 sql 代码来获取两个日期之间的数据?
- php - 即使不存在任何行,也从数据库中获取最新值