runBlocking — qué es, puente bloqueante y cómo funciona

Autor: IT Sectr Publicado: 2026-06-22 Tiempo de lectura: 7 min

runBlocking — un Coroutine Builder en Kotlin que bloquea el hilo actual hasta que finalice la corrutina pasada. A diferencia de launch y async, no es una función suspend y puede llamarse desde código normal (bloqueante). Según la documentación de JetBrains, 2024, runBlocking sirve como puente entre el mundo síncrono y asíncrono, permitiendo ejecutar corrutinas desde la función main y pruebas.

Puntos clave

  • runBlocking — un builder bloqueante que crea un CoroutineScope y espera a que la corrutina termine
  • Bloqueo de hilo — runBlocking retiene el hilo actual hasta que la corrutina y todas sus hijas finalicen
  • Puntos de entrada — main(), pruebas JUnit y puente entre código bloqueante y asíncrono
  • Prohibido en el hilo principal de Android — llamar a runBlocking en el hilo de UI causa ANR
  • Alternativas — lifecycleScope, viewModelScope, TestCoroutineDispatcher para Android

¿Qué es runBlocking?

runBlocking es una función de Kotlin que crea un nuevo CoroutineScope y ejecuta la corrutina pasada, bloqueando el hilo actual hasta que se complete por completo. A diferencia de todos los demás Coroutine Builders, runBlocking no es una función suspend y puede llamarse desde código síncrono normal. La firma de runBlocking toma un CoroutineContext y un bloque suspend, devolviendo un resultado de tipo T.

kotlin
public fun <T> runBlocking(
    context: CoroutineContext = EmptyCoroutineContext,
    block: suspend CoroutineScope.() -> T
): T

runBlocking inicia un nuevo event-loop en el hilo actual. Cuando la corrutina llama a una función suspend (por ejemplo, delay() o await()), runBlocking bloquea el hilo y ejecuta otras corrutinas programadas en el mismo hilo hasta que la suspendida se reanude. Esto es bloqueo cooperativo — el hilo no está inactivo sino que procesa otras corrutinas.

Cómo funciona runBlocking

El mecanismo interno de runBlocking se basa en un event-loop: al llamar a una función suspend, runBlocking pausa la ejecución del bloque actual y ejecuta otras corrutinas de la cola. Cuando la función suspend se completa, la ejecución se reanuda. Este ciclo continúa hasta que todas las corrutinas finalizan.

Event-loop internamente

runBlocking utiliza su propio grupo de un solo hilo para ejecutar corrutinas. A diferencia de Dispatchers.IO o Default, runBlocking no cambia de hilo — maneja todas las corrutinas en el hilo actual, alternando su ejecución. Este es el único builder que garantiza la ejecución en el mismo hilo.

kotlin
fun main() {
    val threadName = Thread.currentThread().getName()
    println("Antes de runBlocking en $threadName")

    val result = runBlocking {
        println("Dentro de runBlocking en ${Thread.currentThread().getName()}")
        delay(500L)
        "Done"
    }

    println("Después de runBlocking: $result")
}

La salida mostrará que los tres println se ejecutan en el mismo hilo. runBlocking no cambia de hilo sino que organiza multitarea cooperativa dentro de un solo hilo usando un event-loop.

Cuándo usar runBlocking

runBlocking está justificado en tres escenarios: el punto de entrada main() en aplicaciones de consola, pruebas unitarias de funciones suspend y puente — llamar a código suspend desde bibliotecas basadas en callbacks o bloqueantes. En código Android de producción, usarlo en el hilo principal está estrictamente prohibido.

EscenarioAplicabilidadRiesgos
main() de aplicación de consolaNinguno — es el punto de entrada, el hilo no bloquea la UI
Pruebas JUnitMínimos — las pruebas son síncronas por definición
Hilo UI de AndroidNoANR, rezagos, congelación de la interfaz
Callback → CorrutinaSí, con precauciónBloqueo del grupo de hilos con operaciones largas

Para pruebas en Android, use kotlinx-coroutines-test con TestDispatcher en lugar de runBlocking. Esto proporciona control de tiempo, limpieza automática y aislamiento de pruebas.

Alternativas a runBlocking

En la mayoría de escenarios, runBlocking puede y debe reemplazarse con alternativas asíncronas. Para Android, estas son viewModelScope, lifecycleScope o CoroutineScope con el dispatcher adecuado. Para pruebas — TestCoroutineDispatcher y runTest.

kotlin
    // Malo: runBlocking en el hilo principal de Android
runBlocking(Dispatchers.Main) {
    val result = networkApi.fetchData()
    textView.setText(result)
}

// Bueno: lifecycleScope
lifecycleScope.launch {
    val result = withContext(Dispatchers.IO) { networkApi.fetchData() }
    textView.setText(result)
}

Para pruebas, el reemplazo es runTest de kotlinx-coroutines-test. Crea un TestCoroutineScope con tiempo virtual, permitiendo probar retrasos sin espera real. Esto acelera las pruebas y las hace deterministas.

Ejemplos de uso de runBlocking

El escenario más común es probar funciones suspend. runBlocking en pruebas permite esperar sincronizadamente un resultado de corrutina sin cambiar la arquitectura. El segundo escenario son bibliotecas con API de callbacks, donde las funciones suspend se llaman desde un contexto bloqueante mediante runBlocking.

kotlin
// Probar función suspend con 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)
    }
}

Para el puente entre callbacks y el mundo suspend, use CompletableDeferred en combinación con runBlocking en lugar de callbacks — esto simplifica las cadenas de operaciones asíncronas y mejora la legibilidad del código.

Peligros del mal uso

El uso inadecuado de runBlocking es uno de los errores comunes al migrar de un enfoque bloqueante a corrutinas. Problemas principales: llamada en el hilo principal de Android, anidamiento de runBlocking, uso dentro de funciones asíncronas y ejecución de operaciones largas con runBlocking.

  • ANR — runBlocking en el hilo principal de Android bloquea el renderizado de UI por más de 5 segundos
  • Deadlock — runBlocking anidado dentro de una corrutina en el mismo hilo lleva a un bloqueo mutuo
  • Confusión con dispatchers — Dispatchers.Main dentro de runBlocking en un hilo de background no tiene Looper y lanza una excepción
  • Fugas de memoria — runBlocking no se cancela automáticamente al destruir Activity/Fragment

Regla de oro: runBlocking es un puente, no un reemplazo. Úselo solo para conectar mundos bloqueantes y no bloqueantes. Para todas las demás tareas, use launch, async o lifecycleScope.

Preguntas frecuentes

¿Por qué runBlocking bloquea el hilo mientras que otros builders no?

runBlocking es el único builder que no es una función suspend. Inicia un event-loop en el hilo actual y no devuelve el control hasta que todas las corrutinas se completan. launch y async devuelven el control inmediatamente, ejecutando la corrutina en segundo plano.

¿Se puede usar runBlocking en Android ViewModel?

No se recomienda. ViewModel tiene un viewModelScope incorporado que gestiona automáticamente las corrutinas y las cancela al destruirse. runBlocking en ViewModel bloquea el hilo y no responde a la cancelación del ciclo de vida.

¿Qué usar en lugar de runBlocking en pruebas unitarias?

Use runTest de la biblioteca kotlinx-coroutines-test. Proporciona un TestCoroutineScope con control de tiempo virtual, cancelación automática y ejecución determinista.

¿Qué es un event-loop en runBlocking?

Event-loop es un ciclo de procesamiento de eventos dentro de runBlocking. Cuando una corrutina se suspende (ej. delay()), el event-loop cambia a ejecutar otras corrutinas listas en el mismo hilo. Esto crea la ilusión de multitarea sin cambio de hilo.

¿Qué sucede cuando se llama a runBlocking dentro de runBlocking?

runBlocking anidado en el mismo hilo crea un deadlock — el bloque externo espera al interno, pero el interno no puede comenzar hasta que el externo termine. En diferentes hilos está permitido pero muy desaconsejado por la complejidad de depuración.

Resumen

  • runBlocking — un Coroutine Builder bloqueante, puente entre código bloqueante y asíncrono
  • Event-loop runBlocking procesa corrutinas cooperativamente en un solo hilo sin cambio
  • Escenarios permitidos — main(), pruebas JUnit, puente desde bibliotecas de callbacks
  • Escenarios prohibidos — hilo UI de Android, llamadas anidadas, operaciones largas
  • Alternativas — lifecycleScope, viewModelScope, runTest para pruebas
  • Riesgo de ANR — runBlocking en el hilo principal congela la app tras 5 segundos
  • Para código Android de producción, use builders asíncronos — runBlocking no está diseñado para UI

Desarrollaremos una aplicación móvil llave en mano

IT Sectr crea aplicaciones para iOS y Android para startups y empresas desde 2017. Le asesoraremos y le propondremos la mejor solución.

Discutir el proyecto

Lea también