Solution: raceOf

suspend fun <T> raceOf( racer: suspend CoroutineScope.() -> T, vararg racers: suspend CoroutineScope.() -> T ): T = coroutineScope { select { (listOf(racer) + racers).forEach { racer -> async { racer() }.onAwait { coroutineContext.job.cancelChildren() it } } } }

Example solution in playground

import kotlinx.coroutines.* import kotlinx.coroutines.selects.select import kotlinx.coroutines.test.currentTime import kotlinx.coroutines.test.runTest import kotlin.coroutines.CoroutineContext import org.junit.Test import kotlin.test.assertEquals suspend fun <T> raceOf( racer: suspend CoroutineScope.() -> T, vararg racers: suspend CoroutineScope.() -> T ): T = coroutineScope { select { (listOf(racer) + racers).forEach { racer -> async { racer() }.onAwait { coroutineContext.job.cancelChildren() it } } } } class RaceOfTest { @Test fun should_wait_for_the_fastest() = runTest { raceOf( { delay(3) }, { delay(1) }, { delay(2) }, ) assertEquals(1, currentTime) } @Test fun should_wait_for_the_fastest_for_big_number() = runTest { val racers = List<suspend CoroutineScope.() -> Long>(1000) { i -> { val num = (i + 100).toLong() delay(num) num } }.shuffled().toMutableList() val result = raceOf(racers.removeFirst(), *racers.toTypedArray()) assertEquals(100, result) assertEquals(100, currentTime) } @Test fun should_respond_with_fastest() = runTest { val result = raceOf( { delay(3000); "C" }, { delay(1000); "A" }, { delay(2000); "B" }, ) assertEquals("A", result) assertEquals(1000, currentTime) } @Test fun should_cancel_slower() = runTest { var slowerJob: Job? = null val result = raceOf( { delay(1000); "A" }, { slowerJob = currentCoroutineContext().job; delay(2000); "B" }, ) assertEquals("A", result) assertEquals(1000, currentTime) assertEquals(true, slowerJob?.isCancelled) } @Test fun should_cancel_when_parent_cancelled() = runTest { var innerJob: Job? = null val job = launch { raceOf( { delay(1000) }, { innerJob = currentCoroutineContext().job; delay(2000) }, ) } delay(500) assertEquals(true, innerJob?.isActive) job.cancel() assertEquals(true, innerJob?.isCancelled) } @Test fun should_propagate_context() = runTest { var innerCtx: CoroutineContext? = null val coroutineName1 = CoroutineName("ABC") withContext(coroutineName1) { raceOf( { delay(1000) }, { innerCtx = currentCoroutineContext(); delay(2000) }, ) } delay(500) assertEquals(coroutineName1, innerCtx?.get(CoroutineName)) val coroutineName2 = CoroutineName("DEF") withContext(coroutineName2) { raceOf( { delay(1000) }, { innerCtx = currentCoroutineContext(); delay(2000) }, ) } delay(500) assertEquals(coroutineName2, innerCtx?.get(CoroutineName)) } }