/* Page prose (de) */
"ClassicModernInPractice" = "# Klassisch und modern in der Praxis\n\n## Die Konzeptkarte\n\nFast jedes klassische Werkzeug hat ein direktes modernes Gegenstück. Wenn du\nalten Code liest oder ihn migrierst, ist das die Übersetzungstabelle:\n\n* Ein Completion Handler, `(Result<T,E>) -> Void`, ➡️ eine `async throws ->\nT`-Funktion. Das Ergebnis kommt *zurück* als Rückgabewert; Fehler `throw`en.\n* `DispatchQueue.global().async { }` ➡️ `Task { }` / der kooperative Pool. Du\nbeschreibst *Arbeit*, nicht *welche Warteschlange*.\n* `DispatchGroup` / `semaphore.wait()` für einen Stapel ➡️ `async let` (feste\nMenge) oder `TaskGroup` (dynamische Menge). Die Verbindung ist strukturell; kein\nZähler auszugleichen.\n* Eine serielle Warteschlange, die gemeinsam genutzten Zustand schützt ➡️ ein\n**`actor`**. Die Isolation wird vom Typ erzwungen, nicht durch Disziplin.\n* `DispatchQueue.main.async { }` ➡️ **`@MainActor`**. Die\nHauptthread-Zugehörigkeit ist Teil der Signatur.\n* `Operation` mit manuellen `isCancelled`-Prüfungen ➡️ kooperativer **`Task`-Abbruch**\n(`Task.isCancelled` / `try Task.checkCancellation()`). Der Abbruch pflanzt sich\nautomatisch den Task-Baum hinunter fort.\n* Ein Delegate oder Callback, das viele Male feuert ➡️ **`AsyncStream`** (oder\neine **Continuation** für einen einmaligen). Ereignisse werden zu einer `for\nawait`-Schleife, aus der du `break`en kannst.\n\nZwei Dinge, die es wert sind, unterstrichen zu werden:\n\nErstens: **`async let` und `TaskGroup` ersetzen beide `DispatchGroup`**: wähle\n`async let`, wenn du die Abrufe im Voraus kennst (wie unsere zwei hier), und\n`TaskGroup`, wenn die Anzahl zur Laufzeit entschieden wird.\n\nZweitens: **der Actor ist der stille Held**: das mit Abstand fehleranfälligste\nDing im klassischen Modell (eine serielle Warteschlange oder ein Semaphor mit\nZählwert 1, das gemeinsam genutzten Zustand schützt und durch das du dich\nerinnern musstest, jeden Zugriff zu leiten) wird zu einem Typ, den der Compiler\nfür dich überwacht.\n\n---\n\n## Wann du das klassische Modell noch verwenden solltest\n\nDas moderne Modell ist der Standard für neuen Code. Aber \"klassisch ist tot\"\nwäre falsch und würde dich zu unnötigen Umschreibungen verleiten. Greif zu (oder\nbehalte) GCD und `Operation`, wenn:\n\n* **Du ältere APIs aufrufst.**  \nViele Frameworks (und dein eigener älterer Code) übergeben dir noch Completion\nHandler oder Delegates. Du *interoperierst* mit ihnen; du musst nicht erst die\nganze Welt umwandeln. (Der saubere Schritt ist, sie zu umhüllen; siehe die\nnächste Seite.)\n* **Du sehr feingranulare Dispatch-Kontrolle brauchst.**  \nEine bestimmte eigene serielle Warteschlange mit präziser QoS, eine\n`DispatchSource` für eine Datei oder einen Timer, ein Barrier-Block auf einer\nnebenläufigen Warteschlange. Diese sind noch legitim und manchmal das\ndirekteste Werkzeug. Der kooperative Pool verbirgt absichtlich die\nThread-Details; wenn du diese Details wirklich brauchst, ist GCD direkt zur\nStelle.\n* **Du OS-Versionen anvisierst, die vor `async`/`await` liegen.**  \nSwift Concurrency läuft auf iOS 13 und später (und den Äquivalenten macOS 10.15,\ntvOS 13, watchOS 6). Da iOS 13 im Jahr 2019 erschien, ist dieser Grund inzwischen\nselten, aber wenn ein Deployment-Target noch unter diese Linie reicht, ist GCD\ndeine portable Option.\n* **Der Code schon funktioniert und du ihn noch nicht migriert hast.**  \nFunktionierendes GCD ist kein Bug. Stabilen Code nur zu modernisieren fügt\nRisiko ohne für den Nutzer sichtbaren Gewinn hinzu. Migriere dort, wo du\nsowieso schon etwas änderst, nicht überall auf einmal.\n\nEhrlich darüber zu sein ist wichtig: das Ziel ist *korrekter, wartbarer* Code,\nkeine zu 100 % moderne Codebasis um ihrer selbst willen.\n\n[Zurück](@previous)  |  Seite 5 von 8  |  [Weiter: Eine pragmatische Migrationsstrategie](@next)";
