Return value only of the faster coroutine

2020-02-28 23:27发布

问题:

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?

回答1:

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.



回答2:

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