SWIFT ASYNC AWAIT NEDİR?

Swift async fonksiyon zaman çizgisi await suspension noktası ve devam noktası yatay akış diyagramı

Swift 5.5 Eylül 2021'de duyurulduğunda, dilin tarihindeki en büyük eşzamanlılık değişikliği gerçekleşti: async/await artık dilin parçasıydı. Bu, Chris Lattner'ın 2017'de yayımladığı Swift Concurrency Manifesto'dan başlayan ve yıllar süren proposal sürecinin sonucuydu. iOS geliştiricileri için bu, Grand Central Dispatch (GCD) ve completion handler'lara dayanan on yıllık bir alışkanlığın kırılma noktası oldu.

GCD Dönemi ve Callback Pyramid Sorunu

2009'da macOS Snow Leopard ile gelen GCD, çok çekirdekli işlemcileri verimli kullanmanın yolunu açtı. Objective-C blokları ve sonrasında Swift closure'ları ile DispatchQueue üzerinden iş parçacığı yönetimi yapıldı. Ancak iç içe geçen completion handler'lar, "pyramid of doom" denilen okunması zor bir yapı doğurdu. Konuya ilişkin proje sayfasını ek bir başvuru kaynağı olarak değerlendirilebilir.

Tipik bir senaryoyu düşünün: kullanıcı oturumu doğrula, profil bilgisini çek, ardından profil görselini indir. Üç adımlık iş, üç iç içe closure demekti. Hata yönetimi her seviyede ayrı ele alınıyor, kontrol akışı parçalanıyordu.

  • Her closure kendi Result<Success, Error> tipini taşıyordu
  • self referansı için sürekli [weak self] capture list yazmak gerekiyordu
  • Erken çıkış ya da iptal mantığı manuel olarak kuruluyordu
  • Test edilebilirlik düşüktü; mock'lamak için protokol katmanları şarttı

Concurrency Proposal Süreci

Swift Evolution sürecinde async/await, tek bir proposal değil bir paket olarak ilerledi. SE-0296 "async/await" temel sözdizimini getirdi; SE-0297 Objective-C köprülemesini, SE-0300 continuation API'lerini, SE-0304 ise structured concurrency'i tanımladı. Bu sıralama önemliydi: dilin yeni anahtar kelimelerini eski callback dünyasıyla uyumlu hale getirmeden, mevcut Foundation API'lerinin tek seferde yeniden yazılması mümkün değildi.

GCD callback pyramid iç içe nested kapanışlar ile async await düz akışlı kod karşılaştırma diyagramı

async/await Sözdiziminin Anatomisi

Bir fonksiyonu async olarak işaretlemek, "bu fonksiyon yürütülürken duraklayabilir" anlamına gelir. Çağrı tarafında await anahtar kelimesi, fonksiyonun süspansiyon noktasını işaretler. Süspansiyon sırasında thread bloklanmaz; çalıştırıcı (executor) o thread'i başka bir işe verir.

func fetchProfile(id: String) async throws -> Profile {
    let session = try await authenticate()
    let profile = try await api.profile(id: id, session: session)
    return profile
}

Bu üç satır, GCD ile yazıldığında üç iç içe closure ve manuel hata propagasyonu gerektirirdi. Burada try ve throws ile birleşen yapı, senkron kodun okunabilirliğini eşzamanlı kod üzerine taşıyor.

Continuation: Eski API'lerle Köprü

Mevcut completion handler tabanlı API'leri async/await dünyasına taşımak için withCheckedContinuation ve withCheckedThrowingContinuation yardımcıları geldi. Bu sayede UIKit, CoreLocation, eski SDK'lar gibi callback bekleyen kütüphaneler async fonksiyonlara sarılabildi. Süreç geçişi tek seferde değil, kademeli oldu.

  1. Eski API'yi continuation.resume(returning:) ile sarmala
  2. Hata yolunu continuation.resume(throwing:) ile yönet
  3. Her continuation tam olarak bir kez resume edilmeli — aksi halde runtime hatası

Structured Concurrency ve Task

async/await tek başına yeterli değildi; paralel iş yürütmek için async let ve TaskGroup yapıları eklendi. Task { } ile senkron bir bağlamdan async dünyaya geçiş yapılır. Structured concurrency'nin temel vaadi: bir parent task tamamlanmadan child task'ları otomatik olarak iptal edilir, scope dışına sızıntı olmaz.

iOS Swift dilini sıfırdan öğrenmek ya da concurrency modelini örnekler üzerinden pekiştirmek isteyenler iOS Swift eğitimi içeriklerinden yararlanabilir.

Actor Modeli ve Veri Yarışları

Çoklu thread'li kodun en eski problemi data race — iki thread aynı veriye eş zamanlı erişip biri yazınca tanımsız davranış oluşur. Swift, bu sorunu derleme zamanında yakalamak için actor tipini getirdi. Actor içindeki değişkenler izole edilir; dışarıdan erişim her zaman async olur ve sıraya alınır.

  • actor: Kendi durumunu koruyan eşzamanlı tip
  • @MainActor: UI güncellemelerinin ana thread'de çalışmasını garantileyen attribute
  • Sendable: Thread'ler arası güvenli geçirilebilir tipleri işaretleyen protokol

GCD'den async/await'e Pratik Geçiş

GCD hâlâ Swift'te mevcut ve düşük seviye iş kuyrukları için geçerli bir araç. Ancak Apple, yeni framework'lerini doğrudan async API'lerle yayımlıyor: URLSession'ın data(from:) async varyantı, SwiftUI'da .task modifier'ı, CoreData'nın perform async sürümü. Geriye dönük uyumluluk için iOS 13+ hedefleyen projelerde async/await kullanılabiliyor — backport sayesinde sadece iOS 15 ile sınırlı değil.

Swift TaskGroup structured concurrency parent task ile çocuk task dallanması scope çerçevesi

Dilin Yönü Nereye Gidiyor

Swift 6 ile birlikte tam strict concurrency checking varsayılan hale geldi. Bu, data race'lerin artık warning değil compile-time hata olarak yakalanması demek. Distributed actor'lar ile ağ üzerinden actor çağrıları, AsyncSequence ile akış tabanlı veri işleme dilin yeni katmanları oldu. async/await'in 2021'deki gelişi, Swift'in sadece "iOS uygulaması yazılan dil" olmaktan çıkıp sistem programlamasına aday olma yolculuğunun başlangıcıydı. iOS geliştirmesine yeni başlayanlar bu modeli baştan öğrenebilir, GCD'yi yalnızca eski kodla karşılaştıklarında inceleyebilirler.