https://kotlinlang.org logo
#coroutines
Title
# coroutines
j

Jaro

03/24/2023, 3:54 PM
Hi all, I'm trying to replicate some
CompletableFuture
code in Java to
Coroutines
in Kotlin, the interface that my Kotlin class implements has functions that are returning
CompletableFuture
Here is example of functions that I want to rewrite
Copy code
private final Map<String, FooWrapper> fooRequests = new ConcurrentHashMap<>();

public CompletableFuture<FooResponse> retrieveFoo(FooRequest request) {
    return completedFuture(...)
        .thenAccept(client::callFoo)
        .thenCompose(ignore -> storeFooRequest(request))
        .whenComplete((response, error) -> log(request, error));
}

private CompletableFuture<FooResponse> storeFooRequest(FooRequest request) {
    var fooWrapper = new FooWrapper(request, new CompletableFuture<>());
    fooRequests.put(request.getId(), fooWrapper);
    return fooWrapper.getResponseFuture();
}
eventually there will be call to other functions that will call
complete
on a
CompletableFuture
stored in
fooRequests
and allowing completion of
retrieveFoo
here is my Kotlin code
Copy code
private val fooRequests: ConcurrentHashMap<String, CompletableDeferred<FooResponse>> = ConcurrentHashMap()

override fun retrieveFoo(request: FooRequest): CompletableFuture<FooResponse> {
    return GlobalScope.future {
        retrieveQuoteAsync(request)
    }
}

private suspend fun retrieveFooAsync(request: FooRequest): Deferred<FooResponse> {
    return coroutineScope {
        storeFooRequest(request)//.await()
    }
}

private suspend fun storeFooRequest(request: FooRequest): Deferred<FooResponse> {
    val deferred: CompletableDeferred<FooResponse> = CompletableDeferred()
    fooRequests[request.id] = deferred
    return deferred
}
I'm trying to replace
CompletableFuture
with
CompletableDeferred
but I do not know how to get the value without calling
await()
, I do not want it to block Any suggestion how to make it work in similar way to Java?
j

Joffrey

03/24/2023, 3:57 PM
Do I understand correctly that you don't control (or don't want to change) the interface that defines return types as
CompletableFuture
? If that's not the case, you should prefer turning your functions into suspend functions instead, and get rid of the futures in return types, it would simplify everything.
j

Jaro

03/24/2023, 4:00 PM
Unfortunately I cant change the interface, it has to be CompletableFuture
j

Joffrey

03/24/2023, 4:00 PM
but I do not know how to get the value without calling await() , I do not want it to block
Calling
await()
doesn't block. It suspends the current coroutine, which is non-blocking (that's the whole point)
j

Jaro

03/24/2023, 4:01 PM
so the "proper" way how to do it in this case is to call
await()
?
j

Joffrey

03/24/2023, 4:02 PM
Yes, if you're in the
future { ... }
coroutine builder already, you can simply await. It will suspend the coroutine created by the
future
builder without blocking any thread.
But also, don't use
GlobalScope
. Use either an existing scope, or create a new scope that is tied to the lifecycle of the component that holds all concurrent coroutines. It probably has a
close()
or
shutdown()
method to stop all concurrent work, and you should use it to cancel all pending requests by cancelling the coroutine scope in which you launched all the coroutines
j

Jaro

03/24/2023, 4:12 PM
is then enough to call
cancel()
on the
coroutineScope
?
j

Joffrey

03/24/2023, 4:21 PM
Yes, that would mark all `CompletableFuture`s returned by
retrieveFoo
as cancelled (
isCancelled == true
), and calling
.get()
on them will throw
JobCancellationException
.
j

Jaro

03/24/2023, 4:31 PM
great, thank you for your help 👍
25 Views