Ahmed Ibrahim
09/11/2020, 12:50 PMJob?
suspend <T> fun executeQuery(query: T) {
val deferred = client.queryCall(query).toDeferred()
currentCoroutineContext()[Job]?.invokeOnCompletion {
if (it is CancellationException) {
deferred.cancel(it)
}
}
deferred.await()
}streetsofboston
09/11/2020, 12:59 PMqueryDeferred does nor toDeferred, but it seems that executeQuery does not do anything in parallel. Just waits for the result of queryDeferred.
suspend fun <T> executeQuery(query: T) {
return queryDeferred(query).toDeferred().await()
}
When the calling scope (CoroutineScope) of executeQuery is cancelled, the Deferred returned by toDeferred will be cancelled too, because of Structured Concurrency (depending on how queryDeferred and toDeferred are implemented….)Ahmed Ibrahim
09/11/2020, 1:00 PMtoDeferred is from Apollo's GraphQL coroutine support.
https://github.com/apollographql/apollo-android/blob/master/apollo-coroutines-support/src/main/kotlin/com/apollographql/apollo/coroutines/CoroutinesExtensions.kt#L82Ahmed Ibrahim
09/11/2020, 1:02 PMqueryGenerator.flatMapLatest { query ->
flowOf(executeQuery(query))
}.collect { }Ahmed Ibrahim
09/11/2020, 1:03 PMgildor
09/11/2020, 1:10 PMgildor
09/11/2020, 1:10 PMgildor
09/11/2020, 1:11 PMgildor
09/11/2020, 1:11 PMgildor
09/11/2020, 1:12 PMAhmed Ibrahim
09/11/2020, 1:14 PMtoFlow().first() or I should roll my own solution until it gets fixed?streetsofboston
09/11/2020, 1:14 PMprivate suspend fun <T> ApolloCall<T>.await(): Response<T> =
suspendCancellableCoroutine { cont ->
enqueue(
object : Callback<T>() {
override fun onResponse(response: Response<T>) {
cont.resume(response)
}
override fun onFailure(e: ApolloException) {
cont.resumeWithException(e)
}
}
)
}
basically, the await() function that Andrey was referring to.Ahmed Ibrahim
09/11/2020, 1:35 PMAhmed Ibrahim
09/11/2020, 1:35 PMstreetsofboston
09/11/2020, 1:36 PMawait() implementation assumes that onResponse is called at most once! For a more general solution, you’d probably want to add some code around the cont.resume(response) to guard for this.Ahmed Ibrahim
09/11/2020, 1:54 PMtoDeferred is designed for a single response and the await solution is intended to replace it?
AFAIK we have toFlow() to handle multiple values.streetsofboston
09/11/2020, 2:43 PMApolloCall<T> is created, when enqueueing the Callback, its onResponse m_ay_ get called more than once. In our app, we know that will never happen.
But, in principle, the onResponse can be called more than once for a given `ApolloCall`… you’d need to guard against that, since cont.resume(value) can be called at most once.