如何创建一个函数,可以从非挂起上下文**多次调用,并且在第一次调用后的特定延迟后,可挂起的函数运行?
我试图在Kotlin中重新创建的JavaScript代码:
const DELAY_MS = 500
const sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms))
const schedulable = () => console.log('schedulable function is running')
let timeout = null
const schedule = () => {
if (!timeout) {
timeout = setTimeout(() => {
timeout = null
schedulable()
}, DELAY_MS)
}
}
;(async () => {
schedule()
await sleep(100)
schedule()
schedule()
await sleep(200)
schedule()
})()
// result: 'schedulable function is running' is printed once
Kotlin尝试(似乎有效,但使用GlobalScope):
object SchedulableAttempt {
private fun schedulable() {
println("schedulable function is running")
}
private const val DELAY_MS = 500L
private var deferred: Deferred<Unit>? = null
fun schedule() {
synchronized(this){
if(deferred === null){
deferred = GlobalScope.async {
delay(DELAY_MS)
deferred = null
schedulable()
}
}
}
}
}
fun main() {
thread {
SchedulableAttempt.schedule()
Thread.sleep(100)
SchedulableAttempt.schedule()
SchedulableAttempt.schedule()
Thread.sleep(200)
SchedulableAttempt.schedule()
}
runBlocking { // just to keep the app running
delay(1000)
}
}
如何在Kotlin中正确地做到这一点?这样的解决方案有没有陷阱?Kotlin文档明确指出避免在非顶级协程的情况下使用GlobalScope,因为它可能会导致内存泄漏
1条答案
按热度按时间mfuanj7w1#
这里有一种方法,即使在
schedulable
中存在异常时,也应该是健壮的,我将在下面描述另一种边缘情况。你不需要从schedule
返回任何东西,我只是像这样做实验。如果你运行它,你可能会看到这样的输出:
也就是说,
delaying
只出现一次。这意味着协程甚至在有机会启动 * 之前就被cancelAndJoin
* 取消了。在这些情况下,协程不会调用reset
,这就是为什么schedule
也处理coroutine?.isCompleted == true
的情况。我最初在launch
ed逻辑中使用了try-finally,但这没有帮助,如果协程从未启动,那么finally甚至没有“排队”。顺便说一句,如果你想在JVM中使用
future
扩展,并且你使用的kotlinx-coroutines-core
版本比1.7.0早,你需要kotlinx-coroutines-jdk8
。