When writing a suspend function, do we make any as...
# coroutines
j
When writing a suspend function, do we make any assumptions about whether or not that function is already being executed in a given scope? Say I'm writing a function which needs to do some file reading and then perform some UI stuff. Should I explicitly use
withContext
for every switch between UI and background? Currently I have:
Copy code
suspend fun openView(path: Path): MyView {
    // Load data

    return withContext(Dispatchers.Swing) {
        // Create UI View
    }
}
s
Don't switch when it is not really necessary. Switch only when it is, eg switch to Dispatchers.IO when the code inside your suspend function is blocking, to make sure that the caller of your suspend function won't get blocked. Trust that the suspend functions your new suspend function calls do not necessarily block on the thread your suspend function is running on.
Copy code
suspend fun loadData(path: Path): Data {
    return withContext(Dispatchers.IO) { ... load data in a blocking way ... }
}

suspend fun openView(path: Path): MyView {
    val data = loadData(path)

    return {
        // Create UI View using 'data'
    }
}
Assuming that
openView
is called from the main ui thread (Dispatchers.Swing)
j
I see. If I'm 100% certain that openView isn't called from the UI thread, is my current impl okay, or still bad practice in general?
s
The
loadData
will be called from the UI thread, but as soon as the execution enters the
loadData
body, then it switches immediately to Dispatchers.IO and the main UI thread will suspend but it won't be blocked.
j
Sorry, I edited my message
s
Still,
openView
should be called from the UI thread, since its purpose seems to be to create a View, a UI....
But if this is how it is structured, then
Copy code
suspend fun loadData(path: Path): Data {
    return withContext(<http://Dispatchers.IO|Dispatchers.IO>) { ... load data in a blocking way ... }
}

suspend fun openView(path: Path): MyView {
    val data = loadData(path)

    return withContext(Dispatchers.Swing) {
        // Create UI View using 'data'
    }
}
should work. Even if you're certain that
openView
is not called from the main UI thread, the
loadData
function wouldn't know that. It should still switch to the Dispatchers.IO
But background processes/threads/dispatchers should not create UI. Your code should be initiated by the UI (thread):
Copy code
// Running on the UI thread.
...
val data = loadDataInBackground(input)
// ... still running on UI thread here ...
val view = View.createdFromData(data)
// ... still running on UI thread here ...
return view
At least, that is on most UI systems, where UI must be created and manipulated on the main UI thread.
j
Right, that makes sense. I'll adjust my impl so that
openView
is called on the UI thread and go with your suggestion. I can call
openView
inside something like
CoroutineScope(Dispatchers.Swing).launch { ... }
.
👍 1
This cleared things up. Thank you.