Published on

Swiftda AsyncSequence va AsyncStream

Authors

AsyncSequence — oddiy Sequence (Array, Range) ning asinxron versiyasi. Oddiy ketma-ketlikda barcha elementlar tayyor va bir zumda olinadi. AsyncSequence da esa elementlar vaqt o'tishi bilan keladi — masalan, tarmoqdan ma'lumot, sensor o'qishlari, yoki timer signallari. Har bir elementni olish uchun for await loop ishlatiladi.

AsyncStream — o'zingiz yaratadigan AsyncSequence. continuation.yield() bilan qiymatlar yuborasiz, continuation.finish() bilan stream ni tugatansiz. Bu eski callback-based API larni asinxron oqimga aylantirish uchun juda qulay.

AsyncSequence asoslari

Apple ko'p API larini allaqachon AsyncSequence sifatida beradi. URL.lines — faylni satr-satr asinxron o'qiydi (katta fayllar uchun xotira tejaydi). URLSession.bytes — tarmoqdan ma'lumotni bayt-bayt stream qiladi. Bundan tashqari, .map(), .filter(), .prefix() kabi transformatsiyalar ham asinxron ishlaydi.

// ═══════════════════════════════════════
//  URL.lines — faylni satr-satr asinxron o'qish
// ═══════════════════════════════════════
func satrlarniOqish() async throws {
    let url = URL(string: "https://example.com/katta-fayl.txt")!

    // for await — har satr kelguncha kutadi
    for try await satr in url.lines {
        print(satr)
        // Katta faylni xotiraga to'liq yuklamaydi
        // Har satr kelganda qayta ishlaydi
    }
}


// ═══════════════════════════════════════
//  URLSession.bytes — baytlarni stream qilish
// ═══════════════════════════════════════
func yuklanishniKuzatish() async throws {
    let url = URL(string: "https://example.com/rasm.jpg")!
    let (bytes, response) = try await URLSession.shared.bytes(from: url)

    let totalSize = Int(response.expectedContentLength)
    var received = 0

    for try await byte in bytes {
        received += 1
        if received % 1024 == 0 {
            let foiz = Double(received) / Double(totalSize) * 100
            print("Yuklandi: \(Int(foiz))%")
        }
    }
}


// ═══════════════════════════════════════
//  TRANSFORMATSIYALAR — map, filter va boshqalar
// ═══════════════════════════════════════
func filtrlash() async throws {
    let url = URL(string: "https://example.com/log.txt")!

    // filter va map — asinxron ishlaydi
    for try await satr in url.lines
        .filter({ $0.contains("ERROR") })      // Faqat xato satrlari
        .map({ $0.uppercased() })                // Katta harfga
        .prefix(10)                               // Faqat 10 ta
    {
        print("XATO: \(satr)")
    }
}

AsyncStream yaratish

AsyncStream — o'z asinxron ketma-ketligingizni yaratish uchun. Uning asosiy qismi continuation — bu orqali qiymatlar yuboriladi va stream tugatiladi. Masalan, har soniyada son yuboradigan sanagich yoki harorat sensor dan ma'lumot oqimi yaratish mumkin. Stream bekor qilinganda onTermination callback orqali resurslarni tozalash mumkin.

// ═══════════════════════════════════════
//  ASYNCSTREAM — o'z async ketma-ketligingiz
// ═══════════════════════════════════════
func sanagich(gacha: Int, interval: Duration = .seconds(1)) -> AsyncStream<Int> {
    AsyncStream { continuation in
        // continuation — qiymat yuborish uchun
        Task {
            for i in 1...gacha {
                try? await Task.sleep(for: interval)

                // yield — keyingi qiymatni yuborish
                continuation.yield(i)
            }
            // finish — stream tugadi
            continuation.finish()
        }
    }
}

// Ishlatish
Task {
    // for await — har soniya bitta son keladi
    for await son in sanagich(gacha: 5) {
        print("Son: \(son)")
    }
    // 1, 2, 3, 4, 5 — har biri 1 soniya oraliqda
    print("Tugadi!")
}


// ═══════════════════════════════════════
//  SENSOR MA'LUMOTLARI — real-time stream
// ═══════════════════════════════════════
func haroratOqimi() -> AsyncStream<Double> {
    AsyncStream { continuation in
        // Timer har 2 soniyada yangi harorat yuboradi
        let timer = Timer.scheduledTimer(withTimeInterval: 2, repeats: true) { _ in
            let harorat = Double.random(in: 20...35)
            continuation.yield(harorat)
        }

        // Stream bekor qilinganda timer ni to'xtatish
        continuation.onTermination = { _ in
            timer.invalidate()
        }
    }
}

Task {
    for await harorat in haroratOqimi() {
        print("Harorat: \(harorat)°C")
        if harorat > 33 {
            print("⚠️ Issiq!")
            break  // Stream dan chiqish
        }
    }
}

AsyncThrowingStream — xato bilan

Agar stream da xato bo'lishi mumkin bo'lsa — AsyncThrowingStream ishlatiladi. continuation.finish(throwing:) bilan xato yuboriladi va for try await loop to'xtaydi. Bu tarmoq ulanishi uzilishi, sensor xatolari kabi holatlar uchun ideal.

// ═══════════════════════════════════════
//  THROWING STREAM — xato tashlashi mumkin
// ═══════════════════════════════════════
enum StreamXatosi: Error {
    case uzildi
}

func tarmoqOqimi() -> AsyncThrowingStream<String, Error> {
    AsyncThrowingStream { continuation in
        Task {
            for i in 1...10 {
                try? await Task.sleep(for: .seconds(1))

                // 70% ehtimolda muvaffaqiyat
                if Bool.random() && i > 5 {
                    // Xato bilan tugatish
                    continuation.finish(throwing: StreamXatosi.uzildi)
                    return
                }

                continuation.yield("Xabar #\(i)")
            }
            continuation.finish()
        }
    }
}

// for TRY await — xatoni ushlash
Task {
    do {
        for try await xabar in tarmoqOqimi() {
            print(xabar)
        }
        print("Muvaffaqiyatli tugadi")
    } catch {
        print("Stream xato bilan tugadi: \(error)")
    }
}

NotificationCenter bilan AsyncSequence

NotificationCenter.default.notifications(named:) — bildirishnomalarni asinxron kuzatish uchun tayyor AsyncSequence. Ilgari addObserver va removeObserver bilan ishlash kerak edi. Endi for await bilan har bir bildirishnomani avtomatik qabul qilish mumkin.

// ═══════════════════════════════════════
//  NOTIFICATION — asinxron kuzatish
// ═══════════════════════════════════════
import UIKit

func klaviaturaniKuzatish() async {
    let notifications = NotificationCenter.default.notifications(
        named: UIResponder.keyboardDidShowNotification
    )

    // for await — har safar klaviatura chiqganda ishlaydi
    for await notification in notifications {
        if let frame = notification.userInfo?[
            UIResponder.keyboardFrameEndUserInfoKey
        ] as? CGRect {
            print("Klaviatura balandligi: \(frame.height)")
        }
    }
}

🎯 Topshiriq

AsyncStream<String> yarating — har 1 soniyada "Xabar #N" yuborsin, 10 ta xabardan keyin finish(). for await bilan o'qing. Keyin AsyncThrowingStream yarating — 5-xabarda xato tashlasin. do-try-catch bilan ushlang.

Buy mea coffee