Move blocking work at the boundary that owns it
A dispatcher determines where coroutine work executes. On JVM, Dispatchers.Default suits CPU work and Dispatchers.IO accommodates blocking I/O. A suspending API that already manages its own execution does not automatically need an extra IO switch.
import kotlinx.coroutines.*
import java.io.File
suspend fun readText(file: File): String = withContext(Dispatchers.IO) {
file.readText()
}
withContext returns the block's result while preserving structured lifetime. It does not make arbitrary blocking calls instantly cancellable. For appropriate interruptible JVM operations, investigate runInterruptible; other APIs may require their own cancellation mechanism.
Android's Main dispatcher requires Android coroutine integration. Do not assume it is available in a plain JVM console test. Inject dispatchers into code that needs deterministic tests rather than hard-coding all execution choices.
Exercise
Separate a blocking file read from a CPU-heavy parsing step and assign each a documented execution policy. Test the parser without a real file.
Check: adding the suspend modifier to File.readText() in a wrapper alone does not stop it blocking its caller's thread.