非阻塞协程示例

Non-blocking coroutine example

我刚刚开始学习协程并设法编写了一个简单的阻塞协程,旨在重试远程休息服务的处理。 returns 如果他目前无法处理 foo 更新,那我就是个骗子。但我们有一个协议,我将尝试在接下来的 10 秒内更新 foo,间隔为 1 秒:

private fun updateFoo(fooId: String): Foo {
    val updatedFoo = runBlocking {
        return@runBlocking withTimeoutOrNull(10L) {
            var result: Foo?
            do {
                result = try {
                    fooClient.update(fooId)
                } catch (ex: Exception) {
                    null
                }
                if (result != null) {
                    return@withTimeoutOrNull result
                }
                delay(1L)
            } while (result == null)
            return@withTimeoutOrNull result
        }
    }
    return updatedFoo ?: throw IllegalStateException(
        "Could not update Foo ($fooId) with retries.")
}

关键是我运行协程阻塞了。十秒是一个足够大的间隔,所以我想将此方法重构为 运行 coroutine nonBlcking。

我尝试使用 launch { ... } 而不是 运行Blocking,但我得到了 Job,但我想那不是我想要的。谁能帮我写对吗???

假设 fooClient.update 已经是一个挂起函数,那么你的问题似乎真的是关于如何桥接 updateFoo 所在的非协程世界(因为它是一个非挂起函数) 与 fooClient.update.

的协程世界

如果对问题的解释是正确的,那么您将需要使用传统机制从 updateFoo 获取异步结果,例如延迟结果或回调。

如果您的 updateFoo 代码 运行 在 async 块中,您可以 return Deferred<Foo?> 值例如:

  fun updateFoo(fooId: String): Deferred<Foo?> {
    return GlobalScope.async {
      return@async withTimeoutOrNull(10L) {
        var result: Foo?
        do {
          result = try {
            update(fooId)
          } catch (ex: Exception) {
            null
          }
          if (result != null) {
            return@withTimeoutOrNull result
          }
          delay(1L)
        } while (result == null)
        return@withTimeoutOrNull result
      }
    }
  }

然后你可以在你的非协程代码中将其转换为 CompletableFuture 例如

asCompletableFuture()

注意:为了简单起见,我在这里使用了 GlobalScope,但根据您的应用程序的要求,使用显式范围通常是个好主意。

你评论说fooClient.update实际上是一个非挂起的阻塞函数。鉴于此,您应该 运行 它在专门为阻止此类调用而配置的适当线程池中,例如 Dispatchers.IO:

withContext(Dispatchers.IO) {
  update(fooId)
}

当 运行 像这样时,协程将不会阻塞 -- withContext 调用挂起。