Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Return value only of the faster coroutine

How can I run multiple coroutines in parallel and return only the value of the one that finishes first?

Real-life scenario, I have two data sources - Database and API service. I don't care where does the data originate from, I just need it fast. How can I query both Database and API service and cancel the other request when the one finishes?

In RxJava world this would be equal to Amb operator. How can I achieve similar behaviour using coroutines?

like image 438
Antimonit Avatar asked Jul 11 '19 11:07

Antimonit


2 Answers

I came up with following implementation:

suspend fun getFaster(): Int = coroutineScope {
    select<Int> {
        async { getFromServer() }.onAwait { it }
        async { getFromDB() }.onAwait { it }
    }.also {
        coroutineContext.cancelChildren()
    }
}

The coroutineScope acts as a parent to all async calls performed within. After the select finishes we can just cancel the rest.

like image 125
Antimonit Avatar answered Oct 25 '22 23:10

Antimonit


You can use select to write your own amb operator. Something like that:

suspend fun <T> amb(vararg jobs: Deferred<T>): T = select {
    fun cancelAll() = jobs.forEach { it.cancel() }

    for (deferred in jobs) {
        deferred.onAwait {
            cancelAll()
            it
        }
    }
}

You can read more about select expression here

like image 25
Andrei Tanana Avatar answered Oct 25 '22 21:10

Andrei Tanana