/* Page prose (es) */
"ClassicModernInPractice" = "# Clásico y moderno en la práctica\n\n## El mapa de conceptos\n\nCasi toda herramienta clásica tiene una contraparte moderna directa. Cuando lees código antiguo, o lo migras, esta es la tabla de traducción:\n\n* Un completion handler, `(Result<T,E>) -> Void`, ➡️ una función `async throws -> T`. El resultado vuelve *como* un valor de retorno; los errores hacen `throw`.\n* `DispatchQueue.global().async { }` ➡️ `Task { }` / el grupo cooperativo. Describes el *trabajo*, no *qué cola*.\n* `DispatchGroup` / `semaphore.wait()` para un lote ➡️ `async let` (conjunto fijo) o `TaskGroup` (conjunto dinámico). La unión es estructural; no hay contador que equilibrar.\n* Una cola serial usada para proteger estado compartido ➡️ un **`actor`**. El aislamiento lo impone el tipo, no la disciplina.\n* `DispatchQueue.main.async { }` ➡️ **`@MainActor`**. La pertenencia al hilo principal es parte de la firma.\n* `Operation` con comprobaciones manuales de `isCancelled` ➡️ **cancelación cooperativa de `Task`** (`Task.isCancelled` / `try Task.checkCancellation()`). La cancelación se propaga hacia abajo por el árbol de tareas automáticamente.\n* Un delegado o callback que se dispara muchas veces ➡️ **`AsyncStream`** (o una **continuación** para un solo disparo). Los eventos se convierten en un bucle `for await` del que puedes hacer `break`.\n\nDos cosas que vale la pena subrayar:\n\nPrimero, **`async let` y `TaskGroup` reemplazan ambos a `DispatchGroup`**: elige `async let` cuando conoces las obtenciones de antemano (como nuestras dos aquí), y `TaskGroup` cuando el número se decide en tiempo de ejecución.\n\nSegundo, **el actor es el héroe silencioso**: lo más propenso a errores del modelo clásico (una cola serial o un semáforo de cuenta 1 protegiendo estado compartido, por el que tenías que acordarte de enrutar cada acceso) se convierte en un tipo que el compilador vigila por ti.\n\n---\n\n## Cuándo seguir usando el modelo clásico\n\nEl modelo moderno es el predeterminado para código nuevo. Pero \"lo clásico está muerto\" sería erróneo y te llevaría a reescrituras innecesarias. Recurre a (o conserva) GCD y `Operation` cuando:\n\n* **Estás llamando a APIs más antiguas.**  \nMuchos frameworks (y tu propio código más viejo) todavía te entregan completion handlers o delegados. *Interoperas* con ellos; no tienes que convertir el mundo entero primero. (El movimiento limpio es envolverlos; ve la página siguiente.)\n* **Necesitas control de despacho muy fino.**  \nUna cola serial personalizada específica con una QoS precisa, un `DispatchSource` para un archivo o temporizador, un bloque de barrera en una cola concurrente. Estos siguen siendo legítimos, y a veces la herramienta más directa. El grupo cooperativo oculta a propósito los detalles de los hilos; cuando de verdad necesitas esos detalles, GCD está justo ahí.\n* **Apuntas a versiones de SO anteriores a `async`/`await`.**  \nLa concurrencia de Swift corre en iOS 13 y posteriores (y los equivalentes macOS 10.15, tvOS 13, watchOS 6). Como iOS 13 salió en 2019, esta razón ahora es rara, pero si un objetivo de despliegue todavía llega por debajo de esa línea, GCD es tu opción portable.\n* **El código ya funciona y todavía no lo has migrado.**  \nGCD que funciona no es un error. Reescribir código estable solo para modernizarlo añade riesgo sin ganancia visible para el usuario. Migra donde ya estás cambiando cosas, no en todas partes a la vez.\n\nSer honesto sobre esto importa: el objetivo es código *correcto y mantenible*, no una base de código 100 % moderna por sí misma.\n\n[Anterior](@previous)  |  página 5 de 8  |  [Siguiente: Una estrategia de migración pragmática](@next)";
