SC #10: Tarea desacoplada

Published: (January 19, 2026 at 04:18 PM EST)
2 min read
Source: Dev.to

Source: Dev.to

Una tarea desacoplada (Detached Task) ejecuta una operación de forma asíncrona, fuera del contexto de concurrencia estructurado que la envuelve. No heredar este contexto implica que no se heredan:

  • La prioridad del contexto
  • El estado de cancelación del contexto.

Para crear la tarea se usa el método estático Task.detached(name:priority:operation:):

Task.detached {
    // Ejecuta una operación de forma asíncrona fuera del contexto estructurado de concurrencia
}

Ejemplo básico

func detachedTaskExample() async {
    func asyncFunc(_ string: String) async {
        print(string)
    }
    await asyncFunc("\(1)")
    Task.detached {
        await asyncFunc("\(2)")
    }
    await asyncFunc("\(3)")
}

El código anterior puede producir el siguiente resultado, aunque no se garantiza el orden:

1
3
2

Riesgo de cancelación de tarea desacoplada

Considera el siguiente ejemplo, donde longRunningAsyncOperation es una tarea asíncrona que se demora (no termina antes de que se cancele) y detachedTaskExample es una tarea que invoca a longRunningAsyncOperation como subtarea y luego crea un Task.detached para invocarla de forma desacoplada:

func detachedTaskExample() async {
    let outerTask = Task {
        // Esta subtarea sí se va a cancelar
        await longRunningAsyncOperation(1)

        // Esta tarea desacoplada NO hereda el estado de cancelación
        Task.detached(priority: .background) {
            // Por eso este checkCancellation no hace nada
            try Task.checkCancellation()

            // Por eso esta subtarea no se va a cancelar
            await longRunningAsyncOperation(2)
        }
    }
    outerTask.cancel()
}

private func longRunningAsyncOperation(_ id: Int) async {
    do {
        print("Empezando tarea \(id)")
        try await Task.sleep(for: .seconds(5))
        print("Terminé la tarea")
    } catch {
        print("\(#function) - Tarea \(id) falló con error: \(error)")
    }
}

La salida de este ejemplo es:

Empezando tarea 1
longRunningAsyncOperation(_:) - Tarea 1 falló con error: CancellationError()
Empezando tarea 2
Terminé la tarea

En este ejemplo:

  • El primer llamado a longRunningAsyncOperation se cancela correctamente porque se invoca dentro del contexto estructurado del Task cancelado.
  • En el segundo llamado, aunque se ejecute try Task.checkCancellation(), no hay forma de detectar la cancelación porque no heredamos ese estado.

Para cancelar una Task desacoplada, se debe mantener una referencia a ella y cancelarla manualmente. Estas tareas no se cancelan automáticamente cuando la referencia se libera, por lo que es necesario gestionarlas explícitamente.

¿Cuándo usar una tarea desacoplada?

Es apropiado crear una tarea desacoplada en escenarios donde:

  • La operación puede ejecutarse de forma independiente.
  • No requiere conexión al contexto contenedor.
  • Es aceptable que continúe ejecutándose aunque el contenedor haya sido cancelado.

Ejemplo válido (no hay referencia a self):

Task.detached(priority: .background) {
    await DirectoryCleaner.cleanup()
}
Back to Blog

Related posts

Read more »

Approachable Swift Concurrency

Article URL: https://fuckingapproachableswiftconcurrency.com/en/ Comments URL: https://news.ycombinator.com/item?id=46432916 Points: 11 Comments: 0...

SC #11: Task Groups

TaskGroup Un TaskGroup contiene subtareas creadas dinámicamente, que pueden ejecutarse de forma serial o concurrente. El grupo solo se considera terminado cuan...

SC #8: Cancelando un Task

Cancelación de Task en Swift y SwiftUI > Nota: En Swift, cancelar una Task no garantiza que la ejecución se detenga inmediatamente. Cada Task debe comprobar ma...