Kotlin Academy · Lección

Limpieza con finally y NonCancellable

Libere recursos en corrutinas canceladas usando finally y withContext(NonCancellable).

Lección 3 de 413 pasos

Limpieza con finally y NonCancellable es una lección gratuita de Kotlin Academy en CoddyKit. Esta es la lección 3 de 4. Puedes leer la lección completa abajo gratuitamente — luego la practicas en el navegador con un editor de código integrado y un tutor de IA 24/7. Forma parte de la ruta de aprendizaje de Kotlin Academy, y tu progreso se sincroniza en la web y la app de CoddyKit. El curso de Kotlin Academy incluye 4 lecciones en total.

Cancelación y finally

Cuando se cancela una corrutina, los puntos de suspensión lanzan CancellationException. El código de los bloques finally siempre se ejecuta, pero el propio finally se ejecuta en un contexto de corrutina cancelado.

import kotlinx.coroutines.*
fun main() = runBlocking {
    val job = launch {
        try {
            delay(1000)
        } finally {
            println("finally: cleaning up")
        }
    }
    delay(50)
    job.cancel()
    job.join()
}

Problema: suspensión en finally

Si intenta hacer delay u otra suspensión dentro de finally en una corrutina cancelada, se lanza una excepción inmediatamente porque el contexto ya está cancelado.

import kotlinx.coroutines.*
fun main() = runBlocking {
    val job = launch {
        try {
            delay(1000)
        } finally {
            // delay(100) // throws CancellationException here!
            println("Sync cleanup OK; async cleanup needs NonCancellable")
        }
    }
    delay(50); job.cancel(); job.join()
}

withContext(NonCancellable)

Envuelva las llamadas de suspensión de finally con withContext(NonCancellable) para ejecutarlas en un contexto no cancelable incluso después de que se cancele el padre.

import kotlinx.coroutines.*
fun main() = runBlocking {
    val job = launch {
        try {
            delay(1000)
        } finally {
            withContext(NonCancellable) {
                delay(100)  // OK: runs despite cancellation
                println("Async cleanup complete")
            }
        }
    }
    delay(50); job.cancel(); job.join()
}

Cuándo usar NonCancellable

Use NonCancellable para cerrar conexiones de base de datos, vaciar registros, enviar eventos de analítica o realizar cualquier limpieza asíncrona que deba completarse independientemente de la cancelación.

import kotlinx.coroutines.*
suspend fun closeConnection() {
    println("Closing DB connection...")
    delay(50) // simulate async close
    println("Connection closed")
}
fun main() = runBlocking {
    val job = launch {
        try { delay(1000) }
        finally { withContext(NonCancellable) { closeConnection() } }
    }
    delay(50); job.cancel(); job.join()
}

NonCancellable no es un ámbito

NonCancellable es un Job que puede pasar a withContext. No lo use directamente como ámbito de corrutinas; úselo únicamente para la limpieza en finally.

import kotlinx.coroutines.*
fun main() = runBlocking {
    // WRONG usage:
    // launch(NonCancellable) { ... } // launches a coroutine that cannot be cancelled
    // RIGHT usage:
    launch {
        try { delay(500) }
        finally {
            withContext(NonCancellable) {
                println("Safe cleanup")
            }
        }
    }.also { delay(50); it.cancel(); it.join() }
}

Limpieza de varios recursos

Encadene varias operaciones de limpieza dentro de un único bloque withContext(NonCancellable).

import kotlinx.coroutines.*
fun main() = runBlocking {
    launch {
        try {
            delay(1000)
        } finally {
            withContext(NonCancellable) {
                delay(20); println("Step 1: flush cache")
                delay(20); println("Step 2: close socket")
                delay(20); println("Step 3: log audit")
            }
        }
    }.also { delay(50); it.cancel(); it.join() }
}

Patrón try/catch/finally

Combine try-catch para los errores de negocio con finally para la limpieza; este es el patrón estructurado estándar.

import kotlinx.coroutines.*
fun main() = runBlocking {
    launch {
        try {
            delay(100)
            throw RuntimeException("business error")
        } catch (e: RuntimeException) {
            println("Handling: ${e.message}")
        } finally {
            withContext(NonCancellable) {
                println("Always cleanup")
            }
        }
    }.join()
}

Orden de limpieza

Los bloques finally se ejecutan en orden inverso al anidamiento. El try-finally externo se ejecuta después de los internos, igual que en el código habitual de Java/Kotlin.

import kotlinx.coroutines.*
fun main() = runBlocking {
    launch {
        try {
            try {
                delay(1000)
            } finally { println("Inner cleanup") }
        } finally { println("Outer cleanup") }
    }.also { delay(50); it.cancel(); it.join() }
}

use() para recursos AutoCloseable

Para los recursos AutoCloseable, use la extensión use { } de Kotlin, que llama automáticamente a close(), incluso en caso de excepciones o cancelación.

import kotlinx.coroutines.*
class Connection : AutoCloseable {
    init { println("Opened") }
    override fun close() { println("Closed") }
    suspend fun query(): String { delay(50); return "result" }
}
fun main() = runBlocking {
    val result = Connection().use { it.query() }
    println(result)
}

Registro del motivo de cancelación

Recupere el mensaje de cancelación de CancellationException en catch o finally para registrar por qué se detuvo una corrutina.

import kotlinx.coroutines.*
fun main() = runBlocking {
    val job = launch {
        try {
            delay(1000)
        } catch (e: CancellationException) {
            println("Cancelled: ${e.message}")
            throw e
        }
    }
    delay(50)
    job.cancel("user navigated away")
    job.join()
}

Limpieza de CoroutineScope con invokeOnCompletion

Job.invokeOnCompletion registra una devolución de llamada que se ejecuta cuando el trabajo finaliza por cualquier motivo; es una alternativa a finally para la limpieza a nivel del padre.

import kotlinx.coroutines.*
fun main() = runBlocking {
    val job = launch {
        delay(1000)
    }
    job.invokeOnCompletion { cause ->
        if (cause != null) println("Ended with: ${cause::class.simpleName}")
        else println("Completed normally")
    }
    delay(50)
    job.cancel()
    job.join()
}

Comprobación rápida

¿Cuál es el propósito de withContext(NonCancellable) en un bloque finally?

Resumen

finally siempre se ejecuta al producirse una cancelación. Envuelva las llamadas de suspensión dentro de finally con withContext(NonCancellable) para que se completen a pesar de la cancelación. Use use { } para los recursos AutoCloseable.

Gratis para empezar

Aprende Kotlin con un tutor de IA — gratis

Escribe y ejecuta código real en tu navegador, obtén ayuda instantánea de un tutor de IA disponible 24/7 y continúa donde lo dejaste en la web o en la aplicación.

Cursos
51
Lecciones
203

Preguntas frecuentes

¿La lección «Limpieza con finally y NonCancellable» es gratis?

Sí — el texto completo de «Limpieza con finally y NonCancellable» es gratis para leer aquí en la web. Para practicarla de forma interactiva (editor de código integrado y tutor de IA 24/7) y desbloquear el resto del curso de Kotlin Academy, actualiza a CoddyKit PRO. El curso de Kotlin Academy incluye 4 lecciones en total.

¿Qué aprenderé en «Limpieza con finally y NonCancellable»?

Libere recursos en corrutinas canceladas usando finally y withContext(NonCancellable). Practicas Kotlin Academy con código real que ejecutas directamente en el navegador, y un tutor de IA 24/7 responde tus preguntas mientras trabajas en la lección.

¿Necesito experiencia previa para empezar Kotlin Academy?

No se requiere experiencia previa. Kotlin Academy en CoddyKit está estructurado para principiantes hasta estudiantes avanzados, así que puedes empezar aquí o desde el inicio y avanzar a tu ritmo. Esta es la lección 3 de 4.

¿Cuánto tiempo toma la lección «Limpieza con finally y NonCancellable»?

La mayoría de las lecciones de CoddyKit toman alrededor de 5–10 minutos. Cada una es compacta e interactiva, así que avanzas constantemente y retomas exactamente por donde dejaste en la web y la app.

¿Puedo escribir y ejecutar código en esta lección de Kotlin Academy?

Sí. Cada lección de Kotlin Academy incluye un editor de código integrado, así que escribes y ejecutas código real directamente en tu navegador y obtienes retroalimentación instantánea de IA — sin configuración local necesaria.

Todas las lecciones de este curso

  1. Cancelación cooperativa: isActive y ensureActive
  2. withTimeout y withTimeoutOrNull
  3. Limpieza con finally y NonCancellable
  4. Propagación de la cancelación en jerarquías de corrutinas
← Volver a Kotlin Academy