CoroutineScope 取消监听器

nha*_*man 3 kotlin kotlin-coroutines

我正在使用范围的类中执行一些工作:

class MyClass(val scope: CoroutineScope) {

  private val state: StateFlow<Int> = someFlow()
    .shareIn(scope, started = SharingStared.Eagerly, initialValue = 0)

  fun save() {
    scope.launch {
      save(state.value)
    }
  }
}
Run Code Online (Sandbox Code Playgroud)

现在我想在取消范围时进行清理。做这个的最好方式是什么?我可以想出这个,但这听起来不太稳定。

init {
  scope.launch {
    try { delay(10000000000000) }
    finally { withContext(Noncancellable) { save(state.value) } }
  }
}
Run Code Online (Sandbox Code Playgroud)

编辑:我修改了我的代码片段以更好地反映我正在做的事情。流state每秒更新几次,当我调用该save()方法时,我想将状态保存到磁盘(所以我不想每次状态更改时都这样做)。

接下来,我想在取消作用域时(即最后)保存状态。这就是我遇到麻烦的地方。

Jof*_*rey 10

据我所知,没有这样的“onCancellation”机制CoroutineScope。但是,有Job.invokeOnCompletion。因此,如果您确实想这样做,您可以Job从作用域的上下文中提取实例并在那里注册回调:

val job = scope.coroutineContext.job // this fails if there is no job
job.invokeOnCompletion { cause ->
    if (cause is CancellationException) {
        // that's a normal cancellation
    }
}
Run Code Online (Sandbox Code Playgroud)

请注意,可能存在没有作业的协程作用域,因此如果您使用此代码,请确保您没有使用如此奇特的作用域。

现在,一般情况下,在执行需要清理的代码时,可以就地“准备”清理。例如,使用带输入流use { ... }或带块关闭资源finally。这将在取消(或任何其他失败,顺便说一句)时自动兑现,因为范围的取消只会CancellationException在运行的协程内生成 s。

解决此问题的另一种方法是在取消范围的同一位置进行所需的清理。

如果您确实想使用像当前并行协程这样的解决方法,您可以使用awaitCancellation而不是巨大的延迟:

init {
  scope.launch {
    try { awaitCancellation() }
    finally { withContext(Noncancellable) { save(state.value) } }
  }
}
Run Code Online (Sandbox Code Playgroud)

但我觉得它不太吸引人。