如何使用 suspendCoroutine 将 java 7 future 变成 kotlin 挂起函数
how to use suspendCoroutine to turn java 7 future into kotlin suspending function
将 java 7 个 futures 包装在 kotlin 挂起函数中的最佳方法是什么?
有没有办法将返回 Java 7 个期货的方法转换为挂起函数?
对于任意回调或 java 8 个可完成的未来,这个过程非常简单,如下所示:
* https://github.com/Kotlin/kotlin-coroutines/blob/master/kotlin-coroutines-informal.md#suspending-functions
在这些情况下,有一个钩子会在 future 完成时触发,因此它可用于在 future 的值准备就绪(或触发异常)后立即恢复继续。
Java 7 个 futures 但是不公开计算结束时调用的方法。
将 Java 7 未来转换为 Java 8 可完成未来在我的代码库中不是一个选项。
当然,我可以创建一个调用 future.get() 的挂起函数,但这会阻塞,从而破坏了使用协程挂起的总体目的。
另一种选择是将可运行对象提交给新的线程执行器,并在可运行对象内部调用 future.get() 并调用回调。从消费者的角度来看,这个包装器将使代码看起来像 "non-blocking",协程可以挂起,但在幕后我们仍在编写阻塞代码,我们正在创建一个新线程只是为了阻塞它
Java 7 未来阻塞。它不是为异步 API 设计的,也不提供任何方式来安装在未来完成时调用的回调。这意味着没有直接的方法来使用 suspendCoroutine
,因为 suspendCoroutine
是为使用异步回调的 API 而设计的。
但是,如果您的代码实际上 运行ning 在 JDK 8 或更高版本下,那么您拥有的实际 Future
实例很有可能您的代码恰好在 运行 时实现了 CompletionStage
接口。您可以尝试将其转换为 CompletionStage
并使用 kotlinx.coroutines
库的 kotlinx-coroutines-jdk8
模块中的即用型 CompletionStage.await
扩展。
当然 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")
}
因此我们的 Futures 有毫秒级的响应时间完成而无需使用任何额外的线程。
当然,您需要添加一些上限以用作超时,这样您就不会永远等待下去。在这种情况下,我们可以稍微更新一下代码:
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()
}
您现在应该可以通过在同一范围内创建另一个协程来执行此操作,该协程在取消协程时取消 Future。
withContext(Dispatchers.IO) {
val future = getSomeFuture()
coroutineScope {
val cancelJob = launch {
suspendCancellableCoroutine<Unit> { cont ->
cont.invokeOnCancellation {
future.cancel(true)
}
}
}
future.get().also {
cancelJob.cancel()
}
}
}
将 java 7 个 futures 包装在 kotlin 挂起函数中的最佳方法是什么? 有没有办法将返回 Java 7 个期货的方法转换为挂起函数?
对于任意回调或 java 8 个可完成的未来,这个过程非常简单,如下所示: * https://github.com/Kotlin/kotlin-coroutines/blob/master/kotlin-coroutines-informal.md#suspending-functions
在这些情况下,有一个钩子会在 future 完成时触发,因此它可用于在 future 的值准备就绪(或触发异常)后立即恢复继续。
Java 7 个 futures 但是不公开计算结束时调用的方法。
将 Java 7 未来转换为 Java 8 可完成未来在我的代码库中不是一个选项。
当然,我可以创建一个调用 future.get() 的挂起函数,但这会阻塞,从而破坏了使用协程挂起的总体目的。
另一种选择是将可运行对象提交给新的线程执行器,并在可运行对象内部调用 future.get() 并调用回调。从消费者的角度来看,这个包装器将使代码看起来像 "non-blocking",协程可以挂起,但在幕后我们仍在编写阻塞代码,我们正在创建一个新线程只是为了阻塞它
Java 7 未来阻塞。它不是为异步 API 设计的,也不提供任何方式来安装在未来完成时调用的回调。这意味着没有直接的方法来使用 suspendCoroutine
,因为 suspendCoroutine
是为使用异步回调的 API 而设计的。
但是,如果您的代码实际上 运行ning 在 JDK 8 或更高版本下,那么您拥有的实际 Future
实例很有可能您的代码恰好在 运行 时实现了 CompletionStage
接口。您可以尝试将其转换为 CompletionStage
并使用 kotlinx.coroutines
库的 kotlinx-coroutines-jdk8
模块中的即用型 CompletionStage.await
扩展。
当然 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")
}
因此我们的 Futures 有毫秒级的响应时间完成而无需使用任何额外的线程。
当然,您需要添加一些上限以用作超时,这样您就不会永远等待下去。在这种情况下,我们可以稍微更新一下代码:
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()
}
您现在应该可以通过在同一范围内创建另一个协程来执行此操作,该协程在取消协程时取消 Future。
withContext(Dispatchers.IO) {
val future = getSomeFuture()
coroutineScope {
val cancelJob = launch {
suspendCancellableCoroutine<Unit> { cont ->
cont.invokeOnCancellation {
future.cancel(true)
}
}
}
future.get().also {
cancelJob.cancel()
}
}
}