Run two suspend functions in parallel and return when first one returns
Asked Answered
D

2

5

I want to run to suspend functions in parallel and return result once the faster one of them returns result. I tried doing the following, but I think it only returns when both of them have finished and it takes too much time, since I'm waiting for both.

val coroutineScope = CoroutineScope(Dispatchers.Main)
val a = coroutineScope.async {
    a(context)
}
val b = coroutineScope.async {
    b(context)
}

val results = listOf(a, b).awaitAll()

return if (results.any { it is RunSuccess }) {
    ...
} else {
    ...
}

Any ideas?

Deportment answered 25/8, 2022 at 10:5 Comment(0)
J
5

You can use select as follows :

suspend fun doWork(): String = coroutineScope {
   select<String> {
      async { work1() }.onAwait { it }
      async { work2() }.onAwait { it }
   }.also {
      coroutineContext.cancelChildren()
   }
}

On this example is returning a String but you can change it with whatever you want, it depends on what your work is returning.

In case you are looking for more functional programming version you can use raceN from Arrow

Where you have this method

public suspend inline fun <A, B> raceN(crossinline fa: suspend CoroutineScope.() -> A, crossinline fb: suspend CoroutineScope.() -> B): Either<A, B> =
  raceN(Dispatchers.Default, fa, fb)

And then you call this raceN method

public suspend inline fun <A, B> raceN(
  ctx: CoroutineContext = EmptyCoroutineContext,
  crossinline fa: suspend CoroutineScope.() -> A,
  crossinline fb: suspend CoroutineScope.() -> B
): Either<A, B> =
  coroutineScope {
    val a = async(ctx) { fa() }
    val b = async(ctx) { fb() }
    select<Either<A, B>> {
      a.onAwait.invoke { Either.Left(it) }
      b.onAwait.invoke { Either.Right(it) }
    }.also {
      when (it) {
        is Either.Left -> b.cancelAndJoin()
        is Either.Right -> a.cancelAndJoin()
      }
    }
  }
Jackjackadandy answered 25/8, 2022 at 10:21 Comment(2)
Thank you. Does the select answer mean that both the processes will run until the end though? I mean, I should just use the first one that finishes as soon as it finishes and the second one should continue running in the background - not stop the running of the second one, right?Deportment
No this first solution with select stops the running of the second oneNoreennorene
O
1

Arrow fx has a nice method called raceN.

https://arrow-kt.io/docs/apidocs/arrow-fx-coroutines/arrow.fx.coroutines/race-n.html

Omor answered 25/8, 2022 at 10:24 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.