https://kotlinlang.org logo
Title
k

Kamila

03/21/2022, 7:13 AM
HI! I am fairly fresh to Kotlin and new to coroutines and flow. I am coming from Java backend so am familiar with threads and completable futures. I am wondering about one scenario, thought I ask here before doing dozens of tests myself: Given external Java API that returns
CompletableFuture
of different entities, for example:
getFoo: CompletableFuture<List<FooDto>> 
getBar: CompletableFuture<List<BarDto>>
getWhatever: CompletableFuture<List<WhateverDto>>
Would it be possible to use coroutines to trigger each of the call in parallel, and combine final object to be:
Object {
    foo: List<FooDto>
    bar: List<BarDto>
    whatever: List<WhateverDto>
}
It is not so easy and obvious to solve with `CompletableFuture`s in Java, maybe it would be easier with
coroutines
? 🤔 The point is I need to fetch multiple different entities before can calculate the final object. I can easily fetch them in sequence, but they are not dependant on each other, so should really be fetched in parallel
r

ritesh

03/21/2022, 7:26 AM
Would it be possible to use coroutines to trigger each of the call in parallel, and combine final object to be:
yes You can use
async
await
in co-routines (similar to
get
) and in the end you can combine the results.
k

Kamila

03/21/2022, 7:36 AM
Just to clarify, I could do
getFoo().get()
getBar().get()
getWhatever.get()
In Java, but this is blocking call. Solution you mention means that all calls will be triggered and the process will wait for each of them to finish, meaning will take as long as the longest running task?
u

uli

03/21/2022, 11:02 AM
Something along these lines (untested). Sure this only works inside a suspend function, as
await
is a suspend function
val fooFuture = getFoo()
val barFuture = getBar()
val whateverFuture = getWhatever()
Object {
  foo = fooFuture.await()
  bar = barFuture.await()
  whatever = whateverFuture.await()
}
r

raulraja

03/21/2022, 2:32 PM
If you want to fail as soon as one of the future values fail you may want to use
awaitAll
https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/await-all.html We have also an encapsulation for this pattern in Arrow that looks like this https://github.com/arrow-kt/arrow/blob/a582273dacbbd8ae52b1acb52253b025720f5877/ar[…]-coroutines/src/commonMain/kotlin/arrow/fx/coroutines/ParZip.kt
k

Kamila

03/21/2022, 2:35 PM
@raulraja but isn't awaitAll expecting all futures to return the same entity type? (My thought when looking at its signature)
r

raulraja

03/21/2022, 2:36 PM
awaitAll
on it’s own does not support multiple typed arguments and if your futures or deferred values are of different type. This is why we have
parZip
otherwise
awaitAll
would have worked. In general Kotlin does not let you abstract over arg arity, but in this operation there are multiple concerns such as cancellation and others that otherwise you need to handle manually
@Kamila this why we have to cast in the link inside the impl of parZip but those are safe yet ugly, If it were me I’d probably encapsulate the pattern for the number of operations you want to use and if you want them to be all cancelled automatically on errors and initiated inside the same structured scope
I think it may look similar to this if the services in Java produces futures.
d

DALDEI

06/05/2022, 10:10 AM
I belive you can just use Any for purposes of awaitAll(), if you have the original future still referenced you can get the return value from that after await/awaitAll returns -- just ignore non-error returns and go back to the original futures and call get() again -- avoiding having to create type specific code just to make awaitAll happy.