Martin Tarjányi
Martin Tarjányi

Reputation: 9947

Is there asyncAll and/or awaitAll operators for Kotlin coroutines?

I have a collection and I want to execute some operations on all items of it asynchronously in Kotlin.

I can do this easily with two map operations:

suspend fun collectionAsync() = coroutineScope {

    val list = listOf("one", "two", "three")

    list.map { async { callRemoteService(it) } }.map { it.await() }.forEach { println(it) }
}

suspend fun callRemoteService(input: String): String
{
    delay(1000)
    return "response for $input"
}

What I would like to have is something like this:

asyncAll(list, ::callRemoteService).awaitAll()

I probably could implement it with extension functions. I'm just wondering if there is a more idiomatic way of doing this.

EDIT: I found that awaitAll already exists. Now, I just need an asyncAll.

list.map { async { callRemoteService(it) } }.awaitAll().forEach { println(it) }

EDIT2: I wrote my asyncAll implementation:

fun <T, V> CoroutineScope.asyncAll(
    items: Iterable<T>,
    function: suspend (T) -> V
): List<Deferred<V>>
{
    return items.map { async { function.invoke(it) } }
}

So now I have this which looks pretty good:

asyncAll(list) { callRemoteService(it) }.awaitAll()

Now, I'm just wondering if it is something that already exists :)

EDIT3: Thinking about it, this might even could look better:

list.asyncAll { callRemoteService(it) }.awaitAll()

I'm just having trouble with the implementation. Since I already have a receiver here which is the iterable, I'm not sure how I could pass the couroutine scope:

fun <T, V> Iterable<T>.asyncAll(
    function: (T) -> V
): List<Deferred<V>>
{
    return this.map { async { function.invoke(it) } }
}

Upvotes: 4

Views: 6082

Answers (1)

Martin Tarj&#225;nyi
Martin Tarj&#225;nyi

Reputation: 9947

Finally, got the solution I wanted. I need this extension function:

suspend fun <T, V> Iterable<T>.asyncAll(coroutine: suspend (T) -> V): Iterable<V> = coroutineScope {
    [email protected] { async { coroutine(it) } }.awaitAll()
}

and I can use it like this:

list.asyncAll { callRemoteService(it) }.forEach { println(it) }

I'm not sure about the naming. It could be asyncMap as well.

Upvotes: 7

Related Questions