Published on

AsyncStream qanday ishlatiladi?

Authors

Nega Continuations har doim ham yordam bermaydi?

Oldingi darslarimizdan birida biz eski "Completion Handler" li funksiyalarni withCheckedContinuation yordamida Async/Await ko'rinishiga o'tkazishni o'rgangan edik.

Lekin o'sha darsda biz bitta qat'iy qoidani aytib o'tgandik: Continuation faqat bitta qiymatni, faqat bir marta qaytarishi shart! Xo'sh, agar bizning eski funksiyamiz (masalan qandaydir taymer yoki Firebase ma'lumotlar bazasini doimiy kuzatib turuvchi funksiya) qiymatlarni 10 marta yoki cheksiz marta qaytarsa-chi? U holda Continuations xato beradi (Crash bo'ladi).

Bunday hollarda bizga yordamga AsyncStream (Asinxron Oqim) yetib keladi.

AsyncStream orqali ma'lumot uzatish

Deylik, bizda har soniyada bittadan son qaytaradigan eski uslubdagi funksiya bor:

class DataManager {
    // Eski usul: completion handler bir necha marta chaqiriladi
    func getContinuousData(completion: @escaping (Int) -> Void) {
        let items = [1, 2, 3, 4, 5]
        for item in items {
            DispatchQueue.main.asyncAfter(deadline: .now() + Double(item)) {
                completion(item)
            }
        }
    }
}

Agar biz ushbu funksiyani asinxron (async) ko'rinishga o'tkazmoqchi bo'lsak, endi undan bitta oddiy son emas, balki sonlarning oqimi (AsyncStream<Int>) qaytishini bildirib qo'yamiz.

class DataManager {

    // ... tepadagi eski funksiya ...

    // Yangi asinxron funksiya (AsyncStream qaytaradi)
    func getAsyncStream() -> AsyncStream<Int> {
        // Oqimni e'lon qilamiz: U faqat Int tipidagi ma'lumotlarni qabul qiladi
        AsyncStream(Int.self) { continuation in

            // Eski funksiyani chaqiramiz
            getContinuousData { newValue in
                // 💡 ENG ASOSIY QISMI:
                // Kelgan har bir yangi qiymatni oqimga (.yield orqali) uloqtiramiz
                continuation.yield(newValue)

                // Agar oqim o'z ishini butunlay tugatsa:
                // continuation.finish() chaqirish mumkin
            }
        }
    }
}

Oqimni tinglash (For await)

Endi oqimni yaratib oldik. Xo'sh ViewModel'da undan qanday foydalanamiz? Bu juda oson! Xuddi Combine'dagi Publisher'larni kuzatganimizdek, bu yerda oqimdagi kelayotgan ketma-ket ma'lumotlarni Asinxron Sikl (for await) orqali tinglaymiz:

@MainActor
class ViewModel: ObservableObject {
    @Published var currentNumber: Int = 0
    let manager = DataManager()

    func startListening() {
        Task {
            // Oqimdan kelayotgan har bir yangi sonni tutib olamiz
            for await value in manager.getAsyncStream() {
                // UI avtomatik yangilanib boradi: 1.. 2.. 3.. 4.. 5..
                self.currentNumber = value
            }
        }
    }
}

Xatolik otadigan oqimlar (AsyncThrowingStream)

Xuddi Continuations kabi, agar sizning eski funksiyangiz nafaqat ma'lumotni, balki xatoliklarni (Error) ham qaytarish ehtimoli bo'lsa, oddiygina AsyncStream o'rniga AsyncThrowingStream dan foydalanasiz:

func getThrowingStream() -> AsyncThrowingStream<Int, Error> {
    AsyncThrowingStream { continuation in
        // ... xato yuz berganda:
        // continuation.finish(throwing: xato)
    }
}

Uni ekranda ishlatganda esa oldiga shunchaki try so'zi qo'shiladi: for try await value in ...

Xulosa

AsyncStream – bu Swift Concurrency'ning eng kuchli qurollaridan biri. U orqali Combine freymvorkini umuman ishlatmasdan turib, real-vaqt rejimida keladigan har qanday ma'lumotlarni (lokatsiya o'zgarishi, taymerlar, chat xabarlari, Firebase kuzatuvlari) oson va toza asinxron kod bilan boshqarishingiz mumkin!

Buy mea coffee