runBlocking — apa itu, jembatan pemblokir dan cara kerjanya

Penulis: IT Sectr Diterbitkan: 2026-06-22 Waktu membaca: 7 mnt

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 — builder pemblokir yang membuat CoroutineScope dan menunggu penyelesaian coroutine
  • Pemblokiran thread — runBlocking menahan thread saat ini hingga coroutine dan semua anaknya selesai sepenuhnya
  • Titik masuk — main(), pengujian JUnit dan jembatan antara kode blocking dan async
  • Dilarang di Main-Thread Android — pemanggilan runBlocking di thread UI menyebabkan ANR
  • Alternatif — lifecycleScope, viewModelScope, TestCoroutineDispatcher untuk Android

Apa itu runBlocking?

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.

kotlin
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.

Cara kerja runBlocking

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.

Event-loop di balik layar

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.

kotlin
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.

Kapan menggunakan runBlocking

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.

SkenarioDapat digunakanRisiko
main() aplikasi konsolYaTidak ada — ini titik masuk, thread tidak memblokir UI
Pengujian JUnitYaMinimal — pengujian pada dasarnya synchronous
Android UI-ThreadTidakANR, keterlambatan, pembekuan antarmuka
Callback → CoroutineYa, dengan hati-hatiPemblokiran 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.

Alternatif runBlocking

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.

kotlin
    // 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.

Contoh penggunaan runBlocking

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.

kotlin
// 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.

Bahaya penggunaan yang salah

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.

  • ANR — runBlocking di Main-Thread Android memblokir rendering UI lebih dari 5 detik
  • Deadlock — runBlocking bersarang di dalam coroutine pada thread yang sama menyebabkan kebuntuan timbal balik
  • Kebingungan dengan dispatcher — Dispatchers.Main di dalam runBlocking di thread latar belakang tidak memiliki Looper dan gagal dengan exception
  • Kebocoran memori — runBlocking tidak dibatalkan secara otomatis saat Activity/Fragment dihancurkan

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

Mengapa runBlocking memblokir thread sementara builder lain tidak?

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.

Bisakah runBlocking digunakan di Android ViewModel?

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.

Apa pengganti runBlocking dalam pengujian unit?

Gunakan runTest dari library kotlinx-coroutines-test. Ini menyediakan TestCoroutineScope dengan kontrol waktu virtual, pembatalan otomatis, dan eksekusi deterministik.

Apa itu event-loop di runBlocking?

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.

Apa yang terjadi jika runBlocking dipanggil di dalam runBlocking?

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

  • runBlocking — Coroutine Builder pemblokir, jembatan antara kode blocking dan asynchronous
  • Event-loop runBlocking memproses coroutine secara kooperatif dalam satu thread tanpa pergantian
  • Skenario yang diizinkan — main(), pengujian JUnit, jembatan dari library callback
  • Skenario yang dilarang — Android UI-Thread, panggilan bersarang, operasi panjang
  • Alternatif — lifecycleScope, viewModelScope, runTest untuk pengujian
  • Risiko ANR — runBlocking di Main-Thread menyebabkan pembekuan aplikasi setelah 5 detik
  • Untuk kode produksi Android gunakan builder asynchronous — runBlocking tidak dirancang untuk UI

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.

Diskusikan proyek

Baca juga