runBlocking — Coroutine Builder di Kotlin yang memblokir thread saat ini hingga coroutine yang diberikan selesai. Tidak seperti launch dan async, ia bukan fungsi suspend dan dapat dipanggil dari kode biasa (blocking). Menurut dokumentasi JetBrains, 2024, runBlocking berfungsi sebagai jembatan antara dunia synchronous dan asynchronous, memungkinkan menjalankan coroutine dari fungsi main dan pengujian.
Poin Utama
runBlocking — fungsi Kotlin yang membuat CoroutineScope baru dan menjalankan coroutine yang diberikan, memblokir thread saat ini hingga selesai sepenuhnya. Tidak seperti semua Coroutine Builder lainnya, runBlocking bukan fungsi suspend dan dapat dipanggil dari kode synchronous biasa. Signature runBlocking menerima CoroutineContext dan blok suspend, mengembalikan hasil bertipe T.
public fun <T> runBlocking(
context: CoroutineContext = EmptyCoroutineContext,
block: suspend CoroutineScope.() -> T
): T
runBlocking memulai event-loop baru di thread saat ini. Ketika coroutine memanggil fungsi suspend (misalnya delay() atau await()), runBlocking memblokir thread dan menjalankan coroutine lain yang dijadwalkan di thread yang sama hingga yang ditangguhkan dilanjutkan. Ini adalah pemblokiran kooperatif — thread tidak diam, tetapi memproses coroutine lain.
Mekanisme internal runBlocking didasarkan pada event-loop: saat memanggil fungsi suspend, runBlocking menangguhkan eksekusi blok saat ini dan menjalankan coroutine lain dari antrian. Ketika fungsi suspend selesai, eksekusi dilanjutkan. Siklus ini berlanjut hingga semua coroutine selesai.
runBlocking menggunakan pool single-thread miliknya sendiri untuk mengeksekusi coroutine. Tidak seperti Dispatchers.IO atau Default, runBlocking tidak mengganti thread — ia memproses semua coroutine di thread saat ini, menyelingi eksekusinya. Ini adalah satu-satunya builder yang menjamin eksekusi di thread yang sama.
fun main() {
val threadName = Thread.currentThread().getName()
println("Sebelum runBlocking di $threadName")
val result = runBlocking {
println("Di dalam runBlocking di ${Thread.currentThread().getName()}")
delay(500L)
"Done"
}
println("Setelah runBlocking: $result")
}
Hasilnya akan menunjukkan bahwa ketiga println dieksekusi pada satu thread. runBlocking tidak mengganti thread, tetapi mengatur multitasking kooperatif dalam satu thread melalui event-loop.
runBlocking dibenarkan dalam tiga skenario: titik masuk main() di aplikasi konsol, pengujian unit fungsi suspend, dan jembatan — memanggil kode suspend dari library berbasis callback atau blocking. Dalam kode produksi Android, penggunaan di thread utama sangat dilarang.
| Skenario | Dapat digunakan | Risiko |
|---|---|---|
| main() aplikasi konsol | Ya | Tidak ada — ini titik masuk, thread tidak memblokir UI |
| Pengujian JUnit | Ya | Minimal — pengujian pada dasarnya synchronous |
| Android UI-Thread | Tidak | ANR, keterlambatan, pembekuan antarmuka |
| Callback → Coroutine | Ya, dengan hati-hati | Pemblokiran pool thread pada operasi yang lama |
Untuk pengujian Android gunakan kotlinx-coroutines-test dengan TestDispatcher sebagai pengganti runBlocking. Ini memberikan kontrol atas waktu, reset otomatis, dan isolasi pengujian.
Di sebagian besar skenario, runBlocking dapat dan harus diganti dengan alternatif asynchronous. Untuk Android, ini adalah viewModelScope, lifecycleScope, atau CoroutineScope dengan dispatcher yang tepat. Untuk pengujian — TestCoroutineDispatcher dan runTest.
// Buruk: runBlocking di thread utama Android
runBlocking(Dispatchers.Main) {
val result = networkApi.fetchData()
textView.setText(result)
}
// Baik: lifecycleScope
lifecycleScope.launch {
val result = withContext(Dispatchers.IO) { networkApi.fetchData() }
textView.setText(result)
}
Pengganti untuk pengujian adalah runTest dari kotlinx-coroutines-test. Ini membuat TestCoroutineScope dengan waktu virtual, memungkinkan pengujian penundaan tanpa menunggu nyata. Ini mempercepat pengujian dan membuatnya deterministik.
Skenario paling umum — menguji fungsi suspend. runBlocking dalam pengujian memungkinkan menunggu hasil coroutine secara synchronous tanpa mengubah arsitektur. Skenario kedua — library dengan callback API, di mana fungsi suspend dipanggil dari konteks blocking melalui runBlocking.
// Uji fungsi suspend dengan runBlocking
class RepositoryTest {
@Test
fun `fetchUser returns correct data`() {
val repository = UserRepository(FakeApi())
val result = runBlocking {
repository.fetchUser("123")
}
assertEquals("John", result.name)
assertEquals("john@test.com", result.email)
}
}
Untuk menjembatani dunia callback dan suspend, gunakan CompletableDeferred dalam kombinasi dengan runBlocking sebagai pengganti callback — ini menyederhanakan rantai operasi asynchronous dan meningkatkan keterbacaan kode.
Penggunaan runBlocking yang salah adalah salah satu kesalahan umum saat beralih dari pendekatan blocking ke coroutine. Masalah utama: pemanggilan di Main-Thread Android, penyarangan runBlocking, penggunaan di dalam fungsi asynchronous, dan menjalankan operasi panjang melalui runBlocking.
Aturan emas: runBlocking adalah jembatan, bukan pengganti. Gunakan hanya untuk menghubungkan dunia blocking dan non-blocking. Untuk semua tugas lain, gunakan launch, async, atau lifecycleScope.
Pertanyaan yang Sering Diajukan
runBlocking adalah satu-satunya builder yang bukan fungsi suspend. Ia memulai event-loop di thread saat ini dan tidak mengembalikan kontrol hingga semua coroutine selesai. launch dan async segera mengembalikan kontrol, menjalankan coroutine di latar belakang.
Tidak disarankan. ViewModel memiliki viewModelScope bawaan yang secara otomatis mengelola coroutine dan membatalkannya saat dihancurkan. runBlocking di ViewModel memblokir thread dan tidak bereaksi terhadap pembatalan lifecycle.
Gunakan runTest dari library kotlinx-coroutines-test. Ini menyediakan TestCoroutineScope dengan kontrol waktu virtual, pembatalan otomatis, dan eksekusi deterministik.
Event-loop — siklus pemrosesan peristiwa di dalam runBlocking. Ketika coroutine ditangguhkan (misalnya delay()), event-loop beralih ke eksekusi coroutine lain yang siap di thread yang sama. Ini menciptakan ilusi multitasking tanpa pergantian thread.
runBlocking bersarang dalam satu thread menciptakan deadlock — blok eksternal menunggu blok internal, tetapi blok internal tidak dapat dimulai sampai blok eksternal selesai. Di thread yang berbeda, ini dapat diterima tetapi sangat tidak disarankan karena sulitnya debugging.
Kesimpulan
Kami akan mengembangkan aplikasi seluler turnkey
IT Sectr membuat aplikasi iOS dan Android untuk startup dan bisnis sejak 2017. Kami akan memberi saran dan mengusulkan solusi terbaik.
Baca juga