Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Getting "Suspension functions can be called only within coroutine body" when calling withContext(Dispatchers.Main) inside a lambda

Tags:

I am creating a coroutine using the script below:

fun bar(completion: () -> Unit) {   GlobalScope.launch(Dispatchers.IO) {     val lambda = {       withContext(Dispatchers.Main) { //Suspension functions can be called only within coroutine body         completion()       }     }     foo(lambda)   } }  fun foo(lambda: () -> Unit) {   //...do something heavy   lambda() } 

But I am getting the error Suspension functions can be called only within coroutine body when I call withContext(Dispatchers.Main) because the lambda changes the context. I cannot change foo(lambda: () -> Unit) to foo(lambda: suspend () -> Unit) because it's from an external library.

Any idea of what I can do to call withContext(Dispatchers.Main) inside the lambda created inside the launch context?

  • Kotlin version: 1.3.41
  • Coroutines: 1.3.0-RC

Thank you!

like image 932
Pedro Paulo Amorim Avatar asked Aug 02 '19 15:08

Pedro Paulo Amorim


People also ask

How does coroutine suspension work?

With coroutines, it just suspends and gives the library a continuation with the instruction "Once you've got this data, just send it to the resume function". Then the thread can go do other things. Once the data is there, the thread will be used to resume from the point where the coroutine was suspended.

What is suspend function in Kotlin coroutines?

delay is a special suspending function. It suspends the coroutine for a specific time. Suspending a coroutine does not block the underlying thread, but allows other coroutines to run and use the underlying thread for their code.

Can you run a suspend function outside of a coroutine or another suspending function?

The syntax of a suspending function is similar to that of a regular function except for the addition of the suspend keyword. It can take a parameter and have a return type. However, suspending functions can only be invoked by another suspending function or within a coroutine.

How do you call a suspend in Kotlin?

We just have to use the suspend keyword. Note: Suspend functions are only allowed to be called from a coroutine or another suspend function. You can see that the async function which includes the keyword suspend. So, in order to use that, we need to make our function suspend too.


2 Answers

You can rewrite your bar function like this:

fun bar(completion: () -> Unit) {     GlobalScope.launch(Dispatchers.IO) {         suspendCoroutine<Unit> {             val lambda = {                 it.resume(Unit)             }             foo(lambda)         }         withContext(Dispatchers.Main) {             completion()         }     } }  
like image 62
Andrei Tanana Avatar answered Sep 17 '22 05:09

Andrei Tanana


 fun bar(completion: suspend CoroutineScope.() -> Unit) {     GlobalScope.launch(Dispatchers.IO) {         val lambda : suspend CoroutineScope.() -> Unit =  {             withContext(Dispatchers.Main) { //Suspension functions can be called only within coroutine body                 completion()             }         }         foo(this,lambda)     } }  private suspend fun foo( coroutineScope: CoroutineScope , lambda : suspend CoroutineScope.() -> Unit)  {     lambda.invoke(coroutineScope) }  

also like this

 fun bar(completion: suspend CoroutineScope.() -> Unit) {     GlobalScope.launch(Dispatchers.IO) {         foo(this){             withContext(Dispatchers.Main) { //Suspension functions can be called only within coroutine body                 completion()             }         }     } }  private suspend fun foo( coroutineScope: CoroutineScope , lambda : suspend CoroutineScope.() -> Unit)  {     lambda.invoke(coroutineScope) }  
like image 30
f2A Avatar answered Sep 21 '22 05:09

f2A