Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Kotlin Coroutines: Calling Deferred::await in Sequence::map

why is it not possible to call Deferred::await in the Sequence::map function like it is possible in List::map?

I made a small example

fun example() = runBlocking {

    val list = listOf(1, 2, 3, 4)

    list.map { async { doSomething(it) } }
            .map { it.await() }

    list.asSequence()
            .map { async { doSomething(it) } }
            .map { it.await() }         // Error: Kotlin: Suspension functions can be called only within coroutine body

}

As you can see the last statement does not compile.

like image 942
xani Avatar asked Nov 25 '18 15:11

xani


People also ask

How do you use await in Kotlin coroutines?

In order to migrate to the async/await pattern, you have to return the async() result from your code, and call await() on the Deferred , from within another coroutine. By doing so, you can remove callbacks you used to use, to consume asynchronously provided values.

What is the difference between launch join and async await in Kotlin coroutines?

The launch is basically fire and forget. Async is basically performing a task and return a result. launch{} does not return anything. async{ }, which has an await() function returns the result of the coroutine.

How do you wait for coroutine to finish Kotlin?

We can wait for the coroutine to finish by calling join() on the Job. For example, suppose we have a suspend function to download some files. We can launch this coroutine and capture the resulting job, which we can later use to join — to wait for the operation to complete.

Are coroutines sequential?

by default the methods are executed sequentially one after another. N.B:- codes are sequentially executed within a coroutine by default.


2 Answers

It's because list.map is an inline fun, which it can be because it's an eager operation that returns a new list. An inline fun can tolerate a suspend block because it's more like a macro: it gets expanded into the call site. As long as the call site is within a suspend block, this works fine.

sequence.map, on the other hand, is lazy and it just adds another lambda to the pipeline of operations that will be performed eventually, when you pull items from the sequence.

The closest match to a lazy sequence in the suspendable world is Flow, but it has a different model where you get all the data pushed to you in one go, whereas you can pull items from a lazy sequence one by one.

like image 69
Marko Topolnik Avatar answered Oct 13 '22 02:10

Marko Topolnik


Adding to Marko's answer, which is correct:

Because the sequence is lazy, the await() call might happen after runBlocking is done, essentially. Neither the async call nor the await will happen until you start pulling elements off the list.

And in the case of the code above, nothing pulls the elements out of the sequence, so neither of the mapping operations on the sequence would happen inside the runBlocking block.

like image 42
Geoffrey Wiseman Avatar answered Oct 13 '22 02:10

Geoffrey Wiseman