Let’s assume if have a function which has some par...
# coroutines
m
Let’s assume if have a function which has some part that has to be done on the main thread, and some calculations, but the whole function has to be blocking, not suspending. Is there any benefit from doing:
fun doIt() = runBlocking { value = withContext(Dispatchers.Default) { // some calculations } }
vs just doing everything on the main thread? As I understand,
runBlocking
will still block the main thread (if
doIt
is called from it), is there any practical difference between blocking the main thread by
runBlocking
and by just doing some calculations on the main thread?
d
AFAIK no difference, but usually in most cases you can
launch
and display some kind of progress (or indeterminate progress) indicator and remove it when the calculation is done. That way the user will still have a responsive UI. Unless this calculation is VERY short...
m
Yeah it’s a pretty short caluculation, but good idea, I could do the whole thing in launch and only do the main thread execution by withContext(Main) in my case, as the function itself has no return type
Thank you 🙂
d
I would do the inverse, make the calculation a
suspend fun calc() = withContext(Dispatchers.Default) { .. }
and
launch
on the
CoroutineScope
of the Activity or ViewModel or whatever that is by default on Main...
That way you insure that you don't run any calculations by mistake on the main thread, and the result is always being passed to the Main thread. In this case it might not be necessary, but I think it is a good habit to get into...
m
Yeah that actually sounds cleaner
g
@maxmello why do you need it to block not suspend?
m
is it the same really? the only benefit i know is, for instance, if you want a blocking DB query with Room, you can runBlocking on the main thread, and do sth inside in IO, and Room will allow it. this is a valid use case right ?
d
If that request takes too long, you'll block your UI until room returns the result from the other thread. The way to do what you said is to
launch { showWaiting(true); runQuery(); showWaiting(false); }
@myanmarking
m
yes, it will block, but you wont get a dialog ANR
🙈 1
i know its a bad example, but a valid one if you (for some reason), need a blocking DB query
g
I feel kinda like the hype-man for coroutines: Using runblocking means you can express down-stack functionality with suspend. So if the OP is considering "use run-blocking or dont", I probably would if theres even a single function call between the
runblocking
point and the
DB.query
point. I"m also building a desktop UI'd application that has a reasonably slow persistence system. The pattern that I've developed is like this:
Copy code
fun someUIEventHandler(event: Event){
  event.sourceUIControl.disable = true 
  //with suspension semantics the UI thread will 
  //continue, so the user could potentially click the 
  //same button again, which can get your UI into a 
  //strange way. Better to just disable the button or 
  //whatever that kicked off this process. 

  launch(appropriateScope + Dispatchers.Main){
    try {
      val uiState = ui.getTextAndOtherInfo();
      val query = makeQuery(uiState, otherModelState)
      val result = withContext(Dispachers.Default) {
        DB.execute(query).andMaybeSomeORM()
      } 
      ui.setTextAndOtherState(result)
    }
    finally {
      event.sourceUIControl.disable = false
    }
  }
}
you can load that up with a bunch of helpers --the most useful for us is a
SuspendingDatabaseFacade
that has all the same functions as
DB
but tagged with
suspend
and wrapping the value in
withContext
.