- Published on
Swiftda Structured Concurrency va Continuations
- Authors
- Name
- ShoxruxC
- @iOSdasturchi
Structured concurrency — vazifalar daraxt shaklida ierarxiya hosil qiladi. Ota vazifa bekor qilinsa — barcha bola vazifalar ham avtomatik bekor bo'ladi. Bu xavfsiz va boshqarish oson. async let va TaskGroup structured hisoblanadi.
Unstructured concurrency — Task { } bilan yaratilgan mustaqil vazifalar. Ular ota kontekstga bog'liq emas — qo'lda boshqarish kerak. Task.detached esa butunlay mustaqil — ota ning priority va actor kontekstini ham meros olmaydi.
Continuations — eski callback-based API larni zamonaviy async/await ga o'girish uchun ko'prik. Ko'p Apple framework lari hali callback bilan ishlaydi (masalan, CLLocationManager delegate). withCheckedContinuation shu API larni async funksiyaga aylantiradi.
Structured vs Unstructured
Structured yondashuv xavfsizroq: ota funksiya tugaganda barcha bola vazifalar ham tugaydi, bekor qilish avtomatik. Unstructured yondashuv moslashuvchan: vazifa yaratuvchidan mustaqil ishlaydi, lekin qo'lda cancel() chaqirish va boshqarish kerak.
// ═══════════════════════════════════════
// STRUCTURED — ierarxiya, avtomatik boshqarish
// ═══════════════════════════════════════
func structuredMisol() async {
// async let — structured
// Ota funksiya tugaganda bolalar ham tugaydi
async let birinchi = yuklash(id: 1)
async let ikkinchi = yuklash(id: 2)
async let uchinchi = yuklash(id: 3)
// Hammasi tugashini kutish
let natijalar = await [birinchi, ikkinchi, uchinchi]
print(natijalar)
// Agar shu funksiya bekor qilinsa — 3 ta yuklash ham bekor bo'ladi
}
// withTaskGroup — ham structured
func guruhliMisol() async {
await withTaskGroup(of: String.self) { guruh in
guruh.addTask { await yuklash(id: 1) }
guruh.addTask { await yuklash(id: 2) }
// Guruh tugaganda — barcha task lar tugagan
}
}
// ═══════════════════════════════════════
// UNSTRUCTURED — mustaqil, qo'lda boshqarish
// ═══════════════════════════════════════
class ViewModel {
var task: Task<Void, Never>?
func boshlash() {
// Task { } — unstructured
// Ota kontekst bilan bog'liq emas
task = Task {
await uzoqIsh()
}
}
func toxtash() {
task?.cancel() // Qo'lda bekor qilish kerak!
}
// Task.detached — butunlay mustaqil
func fonIsh() {
Task.detached(priority: .background) {
// Ota kontekst priority ni meros olmaydi
// Actor kontekstini ham meros olmaydi
await self.ogirHisoblash()
}
}
func uzoqIsh() async { /* ... */ }
func ogirHisoblash() async { /* ... */ }
}
func yuklash(id: Int) async -> String {
try? await Task.sleep(for: .seconds(1))
return "Natija #\(id)"
}
Structured vs Unstructured taqqoslash
STRUCTURED (async let, TaskGroup):
┌─ Ota Funksiya ──────────────┐
│ ┌─ async let A ─┐ │
│ └───────────────┘ │
│ ┌─ async let B ─┐ │
│ └───────────────┘ │
│ Ota bekor = A, B bekor ✅ │
│ Ota tugadi = A, B tugadi ✅ │
└─────────────────────────────┘
UNSTRUCTURED (Task { }):
┌─ Yaratuvchi ──────┐ ┌─ Task ──────────┐
│ task = Task { } │───►│ Mustaqil │
│ ... davom etadi │ │ Qo'lda cancel │
└───────────────────┘ └─────────────────┘
Yaratuvchi tugadi ≠ Task tugadi ❌
withCheckedContinuation — callback dan async ga
withCheckedContinuation — eski callback API ni async funksiyaga aylantirish uchun. Asosiy qoida: continuation.resume() faqat 1 marta chaqirilishi kerak! Ikkinchi chaqiruv runtime crash beradi. withCheckedContinuation bu xatoni aniqlaydi va ogohlantiradi. withUnsafeContinuation esa tezroq, lekin tekshirmaydi.
Agar continuation umuman chaqirilmasa — await chaqiruvchi abadiy kutadi va xotira leak bo'ladi.
// ═══════════════════════════════════════
// ESKI API — callback bilan
// ═══════════════════════════════════════
func eskiYuklash(completion: @escaping (String) -> Void) {
DispatchQueue.global().asyncAfter(deadline: .now() + 2) {
completion("Yuklandi!")
}
}
// ═══════════════════════════════════════
// YANGI — async/await ga o'girish
// withCheckedContinuation bilan
// ═══════════════════════════════════════
func yangiYuklash() async -> String {
// Continuation — callback dunyosidan async dunyosiga ko'prik
await withCheckedContinuation { continuation in
// Eski callback API ni chaqirish
eskiYuklash { natija in
// continuation.resume — natijani async dunyosiga uzatish
// ⚠️ FAQAT 1 MARTA chaqirish mumkin!
continuation.resume(returning: natija)
}
}
}
// Endi oddiy async sifatida ishlatish
Task {
let natija = await yangiYuklash()
print(natija) // "Yuklandi!"
}
withCheckedThrowingContinuation — xato bilan
Agar eski API Result<Success, Failure> yoki (data?, error?) formatida natija qaytarsa — withCheckedThrowingContinuation ishlatiladi. resume(returning:) muvaffaqiyat uchun, resume(throwing:) xato uchun chaqiriladi.
// ═══════════════════════════════════════
// ESKI API — natija yoki xato
// ═══════════════════════════════════════
func eskiTarmoqSorovi(
url: String,
completion: @escaping (Result<Data, Error>) -> Void
) {
// ... eski URLSession callback
DispatchQueue.global().asyncAfter(deadline: .now() + 1) {
if url.isEmpty {
completion(.failure(URLError(.badURL)))
} else {
completion(.success(Data()))
}
}
}
// ═══════════════════════════════════════
// ASYNC GA O'GIRISH — throwing continuation
// ═══════════════════════════════════════
func yangiTarmoqSorovi(url: String) async throws -> Data {
try await withCheckedThrowingContinuation { continuation in
eskiTarmoqSorovi(url: url) { result in
switch result {
case .success(let data):
continuation.resume(returning: data)
case .failure(let error):
continuation.resume(throwing: error)
}
// ⚠️ Faqat BIR MARTA — success YOKI failure
}
}
}
// Ishlatish
Task {
do {
let data = try await yangiTarmoqSorovi(url: "https://api.example.com")
print("Yuklandi: \(data.count) bayt")
} catch {
print("Xato: \(error)")
}
}
Delegate API ni async ga o'girish
Ko'p Apple framework lari (CoreLocation, AVFoundation, HealthKit) delegate pattern bilan ishlaydi. Delegate callback ichida continuation.resume() chaqirib, butun delegate API ni bitta async funksiyaga aylantirish mumkin. Continuation ni class property sifatida saqlash kerak — callback chaqirilganda uni ishlatish va nil ga o'rnatish (ikki marta chaqirishdan himoya).
// ═══════════════════════════════════════
// DELEGATE-BASED API — CLLocationManager
// ═══════════════════════════════════════
import CoreLocation
class JoylashuvMenejeri: NSObject, CLLocationManagerDelegate {
private var continuation: CheckedContinuation<CLLocation, Error>?
private let manager = CLLocationManager()
override init() {
super.init()
manager.delegate = self
}
// async API
func joylashuvniOl() async throws -> CLLocation {
try await withCheckedThrowingContinuation { continuation in
self.continuation = continuation
manager.requestLocation()
}
}
// Delegate callback
func locationManager(_ manager: CLLocationManager,
didUpdateLocations locations: [CLLocation]) {
if let location = locations.first {
continuation?.resume(returning: location)
continuation = nil // Tozalash
}
}
func locationManager(_ manager: CLLocationManager,
didFailWithError error: Error) {
continuation?.resume(throwing: error)
continuation = nil
}
}
// Ishlatish — toza async/await
let menejer = JoylashuvMenejeri()
Task {
let joy = try await menejer.joylashuvniOl()
print("Joylashuv: \(joy.coordinate)")
}
🎯 Topshiriq
Timer.scheduledTimer (callback-based) ni withCheckedContinuation bilan o'rab, sonniKut(soniya:) async funksiya yarating. 3 soniya kutib String qaytarsin. Keyin delegate-based API uchun continuation saqlash va chaqirish misolini yozing.