hey! Is there any way when using ```awaitAll()``` ...
# getting-started
x
hey! Is there any way when using
Copy code
awaitAll()
with different variables of different types for it to not be casted as Any?
👍 1
j
Union types are not a thing in Kotlin yet, I guess that's what you're looking for?
x
perhaps that's it
basically i make 3 api calls and put them in a list
and wanted to await and do something like val (call1, call2) = awaitAll(call1Request, call2Request)
rather than just doing
Copy code
val call1 = call1Request.await()
e
without heterogeneous lists and some fancy type operators, you can only specialize on specific sizes. for example,
Copy code
suspend fun <A, B> Pair<Deferred<A>, Deferred<B>>.awaitAll(): Pair<A, B> = Pair(first.await(), second.await())
suspend fun <A, B, C> Triple<Deferred<A>, Deferred<B>, Deferred<C>>.awaitAll(): Triple<A, B, C> = Triple(first.await(), second.await(), third.await())
x
does three awaits like that do exactly the same that an await all would traditionally do on a list?
🚫 1
three is good enough for me 😃
e
yes, it does
j
@ephemient not really,
awaitAll
on a list of deferred fails on the earliest failure, regardless of order in the list. Using multiple await forces you to wait for all preceding tasks to finish before failing on the await that fails
x
that's unfortunate
e
oh that's true. in most cases you'll end up with similar behavior, if the deferred comes from async in the same coroutine scope, but not necessarily
perhaps you could get away with
Copy code
suspend fun <A, B, C> Triple<Deferred<A>, Deferred<B>, Deferred<C>>.awaitAll(): Triple<A, B, C> = try {
    Triple(first.await(), second.await(), third.await())
} catch (e: Throwable) {
    first.cancel()
    second.cancel()
    third.cancel()
    throw e
}
or just cast away,
Copy code
suspend fun <A, B, C> Triple<Deferred<A>, Deferred<B>, Deferred<C>>.awaitAll(): Triple<A, B, C> = listOf(first, second, third).awaitAll().let { (first, second, third) -> Triple(first as A, second as B, third as C) }
j
Mmh I don't think the `catch`+
cancel
approach solves the problem because the time that you spend waiting before the failure is the problem, not the time after
But you're right that if those deferred come from the same scope with a regular (non supervisor) job, one failure should cancel the others anyway automatically
e
yeah the catch+cancel doesn't propagate in the same order, you'd need to drop down to a lower level with
invokeOnCompletion
to preserve it
but how much that matters, depends on the use
g
Using multiple await forces you to wait for all preceding tasks to finish before failing on the await that fails
No, if you use own scope. This why I prefer (and use) this version:
Copy code
suspend fun <A, B, C> awaitTriple(
        a: suspend () -> A,
        b: suspend () -> B,
        c: suspend () -> C,
): Triple<A, B, C> {
    return coroutineScope {
        val aDeferred = async { a() }
        val bDeferred = async { b() }
        val cDeferred = async { c() }
        Triple(
                aDeferred.await(),
                bDeferred.await(),
                cDeferred.await()
        )
    }
}
👍 1
This is what you usually want anyway, no need to create own scope and less chance to do mistake