如何使用suspendCoroutine将java 7的未来转化为kotlin暂停函数

时间:2018-06-11 08:27:22

标签: future kotlin-coroutines

在kotlin暂停函数中包装java 7期货的最佳方法是什么? 有没有办法将返回Java 7期货的方法转换为挂起函数?

对于任意回调或java 8 completablefutures,该过程非常简单,如下所示: * https://github.com/Kotlin/kotlin-coroutines/blob/master/kotlin-coroutines-informal.md#suspending-functions

在这些情况下,有一个钩子在未来完成时被触发,所以只要未来的值准备就绪(或触发异常),它就可以用来恢复延续。

但是,Java 7期货不公开计算结束时调用的方法。

将Java 7未来转换为Java 8可完成的未来不是我的代码库中的一个选项。

当然,我可以创建一个调用future.get()的挂起函数,但这会阻塞,这会破坏使用协同程序暂停的总体目的。

另一个选择是将runnable提交给新的线程执行程序,并在runnable调用future.get()内部并调用回调。从消费者的角度来看,这个包装器会使代码看起来像“非阻塞”,协程可以暂停,但是我们仍在编写阻塞代码,我们正在创建一个新的线程只是为了阻止它< / p>

3 个答案:

答案 0 :(得分:4)

Java 7的未来是阻止。它不是为异步API设计的,也没有提供任何方法来安装将来完成时调用的回调。这意味着没有直接使用suspendCoroutine的方法,因为suspendCoroutine设计用于使用异步回调的API。

但是,如果您的代码实际上是在JDK 8或更新版本下运行,那么代码中实际的Future实例很可能会实现CompletionStage接口在运行时。您可以尝试将其转换为CompletionStage,并使用kotlinx-coroutines-jdk8kotlinx.coroutines模块中的现成CompletionStage.await扩展名。

答案 1 :(得分:0)

Roman当然是对的,Java Future不允许您在完成工作时提供回调。

但是,它确实为您提供了一种检查工作是否完成的方法,如果已经完成,则调用.get()不会阻塞。

对我们来说幸运的是,我们还有一种廉价的方法来转移线程以通过协程快速进行轮询检查。

让我们编写该轮询逻辑,并将其作为扩展方法出售:

suspend fun <T> Future<T>.wait(): T {
    while(!isDone)
        delay(1) // or whatever you want your polling frequency to be
    return get()
}

然后使用:

fun someBlockingWork(): Future<String> { ... }

suspend fun useWork() {
    val result = someBlockingWork().wait()
    println("Result: $result")
}

因此,我们在不使用任何额外线程的情况下就可以完成毫秒级的响应时间。


当然,您将需要添加一些上限以用作超时,这样您就不会永远等待下去。在这种情况下,我们可以稍微更新一下代码:

suspend fun <T> Future<T>.wait(timeoutMs: Int = 60000): T? {
    val start = System.currentTimeMillis()
    while (!isDone) {
        if (System.currentTimeMillis() - start > timeoutMs)
            return null
        delay(1)
    }
    return get()
}

答案 2 :(得分:0)

您现在应该能够通过在取消协程时取消销毁未来的相同范围内创建另一个协程来做到这一点。

withContext(Dispatchers.IO) {
  val future = getSomeFuture()

  coroutineScope {
    val cancelJob = launch {
      suspendCancellableCoroutine<Unit> { cont ->
        cont.invokeOnCancellation {
          future.cancel(true)
        }
      }
    }  

    future.get().also {
      cancelJob.cancel()
    }
  }
}