英文:
Await Java 5 Futures in Kotlin Coroutines without blocking the Thread
问题
Sure, here's the translated code:
// 从中取得一个`suspend`函数,我希望从中返回Java 5 `Future`的结果。Future对象来自另一个库[Firebase Cloud Firestore- Admin SDK for Java](https://firebase.google.com/docs/firestore/),并提供阻塞调用`get()`来检索所述future的结果。
// 我的函数看起来像这样-
suspend fun getPrefix(messageCreateEvent: MessageCreateEvent): String {
val snapshot = db.collection("prefixes")
.document(messageCreateEvent.guildId.get().asString())
.get() //This returns a future
.get() //Retrieves the future's result (Blocks thread; IDE gives warning)
//Return the prefix
return if (snapshot.exists())
snapshot.getString("prefix") ?: DEFAULT_PREFIX
else DEFAULT_PREFIX
}
## 我考虑过的解决方案 ##
我首先考虑的是在`kotlinx.coroutine`中寻找扩展来连接这些futures。虽然这些扩展是存在的,但它们只适用于`CompletionStatge`。所以我决定将future包装到其中一个中-
```kotlin
val snapshot = CompleteableFuture.supplyAsync {
db.collection("prefixes")
.document(messageCreateEvent.guildId.get().asString())
.get() // This returns a future
.get() // Get the result
}.await()
我经验有限,不确定这是否是正确的解决方案。我在一个编程社区中提出了我的问题,其中一个人建议我使用Deferred
-
val deferred = CompletableDeferred<DocumentSnapshot>()
val future = db.collection("prefixes")
.document(messageCreateEvent.guildId.get().asString())
.get()
future.addListener(
Runnable { deferred.complete(future.get()) },
ForkJoinPool.commonPool()
)
val snapshot = deferred.await()
我已经花了很多时间搜索将futures与协程连接的方法,甚至在SO上都没有类似的问题。尽管如此,如果这个问题被标记为重复,我也不会感到惊讶。
Please let me know if you need any further assistance!
<details>
<summary>英文:</summary>
I have a `suspend` function from which I want to return the result of a Java 5 `Future`. The future object comes from another library [Firebase Cloud Firestore- Admin SDK for Java](https://firebase.google.com/docs/firestore/) and provides a blocking call `get()` to retrieve the result of the said future.
My function looks like this-
```kotlin
suspend fun getPrefix(messageCreateEvent: MessageCreateEvent): String {
val snapshot = db.collection("prefixes")
.document(messageCreateEvent.guildId.get().asString())
.get() //This returns a future
.get() //Retrieves the future's result (Blocks thread; IDE gives warning)
//Return the prefix
return if (snapshot.exists())
snapshot.getString("prefix") ?: DEFAULT_PREFIX
else DEFAULT_PREFIX
}
Solutions I have considered
The first thing that I considered was to look in kotlinx.coroutine
for extensions to bridge the futures. While the extensions exist, they do only for CompletionStatge
. So I decided to wrap the future into one ()-
val snapshot = CompleteableFuture.supplyAsync {
db.collection("prefixes")
.document(messageCreateEvent.guildId.get().asString())
.get() // This returns a future
.get() // Get the result
}.await()
I am quite inexperienced and not sure if this is was proper solution. I queried my question on a programming community, where a person recommended me to use a Deferred
-
val deferred = CompletableDeferred<DocumentSnapshot>()
val future = db.collection("prefixes")
.document(messageCreateEvent.guildId.get().asString())
.get()
future.addListener(
Runnable { deferred.complete(future.get()) },
ForkJoinPool.commonPool()
)
val snapshot = deferred.await()
I've give it quite a time to search for a way to bridge futures to co-routines, there isn't even a similar question on SO. Through, I wouldn't be surprised if this question gets a duplicate mark.
答案1
得分: 3
The key to this problem is the suspendCoroutine
function. The other non-obvious bit is that to add a callback to the ApiFuture
, you use a static method on ApiFutures
.
Here's an extension function that implements await()
on an ApiFuture
.
/**
* Function to convert an ApiFuture into a coroutine return.
*/
suspend fun <F : Any?, R : Any?> ApiFuture<F>.await(
successHandler: (F) -> R,
): R {
return suspendCoroutine { cont ->
ApiFutures.addCallback(this, object : ApiFutureCallback<F> {
override fun onFailure(t: Throwable?) {
cont.resumeWithException(t ?: IOException("Unknown error"))
}
override fun onSuccess(result: F) {
cont.resume(successHandler(result))
}
}, Dispatchers.IO.asExecutor())
}
}
/**
* inline function to retrieve a document as a POJO from a DocumentReference
*/
suspend inline fun <reified T: Any> DocumentReference.toObject(): T? {
return get().await<DocumentSnapshot, T?> {
it.toObject(T::class.java)
}
}
Now you can write things like:
suspend fun getUser(id: String): User? {
return db.collection("users").document(id).toObject()
}
英文:
The key to this problem is the suspendCoroutine
function. The other non-obvious bit is that to add a callback to the ApiFuture
you use a static method on ApiFutures
.
Here's an extension function that implements await()
on an ApiFuture
.
/**
* Function to convert an ApiFuture into a coroutine return.
*/
suspend fun <F : Any?, R : Any?> ApiFuture<F>.await(
successHandler: (F) -> R,
): R {
return suspendCoroutine { cont ->
ApiFutures.addCallback(this, object : ApiFutureCallback<F> {
override fun onFailure(t: Throwable?) {
cont.resumeWithException(t ?: IOException("Unknown error"))
}
override fun onSuccess(result: F) {
cont.resume(successHandler(result))
}
}, Dispatchers.IO.asExecutor())
}
}
/**
* inline function to retrieve a document as a POJO from a DocumentReference
*/
suspend inline fun <reified T: Any>DocumentReference.toObject(): T? {
return get().await<DocumentSnapshot, T?> {
it.toObject(T::class.java)
}
}
Now you can write things like:
suspend fun getUser(id: String): User? {
return db.collection("users").document(id).toObject()
}
答案2
得分: 0
你可以使用 kotlinx-coroutines-guava
来实现这个功能。
首先,使用 ApiFutureToListenableFuture
将 ApiFuture
转换为 ListenableFuture
,然后使用 .await
来等待可监听的未来的完成,而无需阻塞线程。
ApiFutureToListenableFuture(this).await()
英文:
You can use kotlinx-coroutines-guava
to do this
First convert ApiFuture
to ListenableFuture
using ApiFutureToListenableFuture
then use .await
to await completion of the listenableFuture without blocking a thread.
ApiFutureToListenableFuture(this).await()
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论