Hello! I've been stuck trying to wrap my head arou...
# announcements
h
Hello! I've been stuck trying to wrap my head around coroutines and suspension functions. Suppose I have a suspending function that takes some time to return, and suppose I launch a coroutine on Dispatchers.Main to run that suspending function. To my understanding, what would happen is that when the execution hits the suspension point of the suspending function, instead of blocking the thread to wait for the function to return, the control returns back to the main thread while the function continues executing in the background. Is this correct? If so, which thread is the suspending function now executing on?
a
Hey! As far as I know, that's not correct. The function will suspend the main thread, since your suspend function is called in a coroutine launched on Dispatchers.Main. You will need to launch another coroutine either with launch or withContext for example, and call the suspending function inside that coroutine
You should read the official guide: https://github.com/Kotlin/kotlinx.coroutines/blob/master/coroutines-guide.md or do the Codelab from Google: https://codelabs.developers.google.com/codelabs/kotlin-coroutines/index.html#0 or watch some videos from KotlinConf/AndroidDevSummit about coroutines 😄
m
And there’s a channel #C1CFAFJSK that may be able to help with more specific questions
h
@arthur.n What exactly does suspending mean, then? Suspending functions can be used to perform computations too. However, if the function is suspended, doesn't it mean that any work that it is doing is paused? However, if the work its doing is paused, then the function will never return, so this can't be true. The coroutine can only continue after this function has returned, because coroutines are sequential by default. If the work is not paused, then it has to be happening somewhere, right? On which thread is the work being carried out, then?
@Mike Okay, I'll post further questions there
a
"However, if the function is suspended, doesn't it mean that any work that it is doing is paused? However, if the work its doing is paused, then the function will never return, so this can't be true. " Like I said, you should read the guide, you are getting the concepts wrong. A suspending function will suspend a coroutine or another suspending function until it finished it's work. it won't suspend the work itself.
h
Okay, I'll give the guide a closer look.
j
I actually think you're not wrong at all - but if you are, I am too, which might very well be the case... 🙂 In my mental model, when a couroutine hits a suspend point (by calling a suspending function), the thread that the coroutine is running on will become available to do other things. When the suspending function returns, the rest of the coroutine will be executed on a thread which may or may not be the thread it originally ran on.
Dispatcher.Main
is a dispatcher that runs all coroutines on the Android UI thread, which is a single thread. But the default dispatcher
Dispatcher.Default
uses at least two threads, and it is very possible that your coroutine will continue on another thread than on which it initially ran.
h
@Joris PZ Thank you for putting this into words! I couldn't describe it the way you did, but that's exactly what I think about how suspending functions work. However, there are still a few things unclear with this model: 1. Which background thread does the function run on after it is suspended? 2. How does the compiler determine the point at which a function is to be suspended and put on a background thread?
j
1. A suspended coroutine isn't 'running' anywhere. Suppose you have this code:
Copy code
suspend myFunction() {
    val x = 1
    val y = otherSuspendingFunction(x)
    println(x + y)
}
At the point where
otherSuspendingFunction
is called,
myFunction
gets suspended until
otherSuspendingFunction
returns. The code below the suspending function call is effectively turned into a callback by the compiler, i.e.
Copy code
otherSuspendingFunction(x) { result -> println(x + result)}
Coroutines are in that sense very much like
async/await
, in that it allows you to write asynchronous code without the syntactic overhead and noise of callbacks or promises. I don't really know if
otherSuspendingFunction
will usually be called on the calling thread, or if it will be scheduled completely independently on any of the threads available to the dispatcher. 2. Coroutines get suspended anytime you call a suspending function. In IntelliJ IDEA you can see these suspension points marked in the gutter with this symbol: ⏸️ Again, I am trying to figure this stuff out myself, so I'll be happily corrected if I got anything wrong here
h
Thank you, your first point helped me make understand it better. About the second point: How do I decide if my function should be marked with
suspend
? Suppose I have a computationally intensive function that doesn't call any other suspending functions. If I call it inside a coroutine, it blocks until the execution is complete. Now if I mark this function with
suspend
, I expect that the coroutine will pause when it reaches this function, and resume when this function returns. However, when I try to do this (mark this function with suspend), I get an IDE warning:
"Redundant 'suspend' modifier: suspend modifier is redundant if no other suspend functions are called inside."
EDIT: I now realize that what I'm expecting by marking the function with
suspend
is wrong.
I think you guys are right, I'm approaching this the wrong way. The computationally expensive function has to be executed somewhere, right? I can't suspend it. The coroutine will block the current thread while this function is executing, so if I'm working in a UI based framework, I need to make sure that I don't launch this coroutine on the main dispatcher.
@Joris PZ Thanks for your help! 😇
j
You're welcome! Check out this blog by @elizarov, it covers large parts of your questions I think: https://medium.com/@elizarov/blocking-threads-suspending-coroutines-d33e11bf4761
h
I wish I had read this post earlier. I too had gotten the wrong idea because of the Exploring Coroutines talk by Venkat Subramaniam. It kinda tripped me up on the usage of 'suspend' in a function that did not call any other suspending functions:
Copy code
suspend fun getStockPrice(ticker: String): String {
    return java.net.URL(
            "<http://localhost:8085?ticker=$ticker>").readText()
}
It is a great talk otherwise, though. Thanks for sharing this!
g
Yes, this example is wrong, I asked about it Venkat after talk
suspend is useless here, because thread will be blocked
you can wrap this code using
withContext(SOME_DISPATCHER)
so it would be safe to call this function without blocking your current thread, but some thread from dispatcher (
SOME_DISPATCHER
) will be blocked. Also problem of this that such operation will not be cancellable, and will be cancelled only when blocking code is finished and function returned. So just for this case (http request) better to use any async API + coroutine dispatcher
h
@gildor Yes, and I this is why in the talk 'Coroutines by Example' on DroidconNYC 18, Christina Lee suggested that we launch our coroutines on the main thread, and move the responsibility of assigning threads of execution to suspending functions. This is because on Android, a very common workflow is to perform an operation a the background thread, and then update the UI thread with the results. Launching coroutines on the main thread makes it easier to do so.
This is applicable to any UI based framework, since they all have a main UI thread which needs to be kept free of long running operation.
g
Not sure how choosing dispatcher for coroutines is related to discussed topic