59

I have two suspend functions:

suspend fun sendData() : Boolean 

suspend fun awaitAcknowledge() : Boolean

and I want to wrap them in a third suspend function in which they should be executed in parallel and I want to calculate the final result by having both return values:

suspend fun sendDataAndAwaitAcknowledge() : Boolean {
    // TODO execute both in parallel and compare both results
}

However, if I write it like that,

suspend fun sendDataAndAwaitAcknowledge() : Boolean {
    val sendResult = sendData()
    val receiveAck = awaitAcknowledge()
}

the functions will be executed in a serial order, which will not work in my case.

Coming from RxJava, I would like to achieve something like the zip operator:

Single.zip(awaitAcknowledge(), sendData(), {receiveAck, sendResult -> ...})

How can I do this with Coroutines?

Christopher
  • 9,682
  • 7
  • 47
  • 76

2 Answers2

58

You can use awaitAll for that purpose:

import kotlinx.coroutines.*

suspend fun sendDataAndAwaitAcknowledge() = coroutineScope {
    awaitAll(async {
        awaitAcknowledge()
    }, async {
        sendData()
    })
}

fun sendData() = true

fun awaitAcknowledge() = false

fun main() {
    runBlocking {
        println(sendDataAndAwaitAcknowledge()) // [false, true]
    }
}
Alexey Soshin
  • 16,718
  • 2
  • 31
  • 40
  • It is possible to execute all functions at the same time without a milliseconds delay? – RaJ Mar 28 '23 at 07:26
10

This is the general pattern:

suspend fun sendDataAndAwaitAck() = coroutineScope {
  val one = async { sendData() }
  val two = async { awaitAck() }
  println("The result is ${one.await()}, ${two.await()}")
}

The two suspending functions (sendData and awaitAck) are called from within another suspending function (sendDataAndAwaitAck). They could potentially run in parallel, depending on the environment and CPU setup. The outer suspending function will wait for the two inner functions to finish their tasks before proceeding.

Note 1: async is available only in a Coroutine Scope.

Note 2: if you don't want the whole scope to fail when one of the child coroutines fails, you should use supervisorScope instead.

milosmns
  • 3,595
  • 4
  • 36
  • 48
  • 16
    you cannot just use `async`. You need coroutine scope for it – Andrei Tanana Aug 12 '19 at 07:39
  • @AndreiTanana check the link. https://kotlinlang.org/docs/reference/coroutines/composing-suspending-functions.html –  Aug 12 '19 at 07:55
  • 1
    this is the full code of this example https://github.com/kotlin/kotlinx.coroutines/blob/master/kotlinx-coroutines-core/jvm/test/guide/example-compose-02.kt it uses `runBlocking` scope – Andrei Tanana Aug 12 '19 at 08:03
  • 3
    Hmm, when I try to implement in this way, I get `unresolved reference` on the `async` keyword. However, I don't get an import suggestion from Android Studio. – Christopher Aug 12 '19 at 08:48
  • 3
    The `async` being used here is a static import from `GlobalScope`, which is not a great thing to use. Async should be launched off of a more granular coroutine scope. – afollestad May 19 '22 at 00:47