Published on

TaskGroup orqali ommaviy asinxron ishlarni bajarish

Authors

Nega TaskGroup kerak?

Oldingi darsda biz 4 ta rasmni async let orqali bir vaqtda yuklashni o'rgangan edik. Lekin haqiqiy ilovalarda (masalan ijtimoiy tarmoq tasmalarida) bizda yuzlab postlar yoki rasmlar bo'lishi mumkin. Agar massiv (array) ichida 50 ta element bo'lsa va biz ularning barchasini o'sha vaqtning o'zida paralel yuklamoqchi bo'lsak, async let bilan buni qilish ilojsiz yoki kodi juda xunuk bo'ladi.

Shunday katta hajm va moslashuvchanlik (scalability) talab qilinganda biz TaskGroup'dan foydalanamiz.

TaskGroup turlari

Swift bizga asosan 2 xil TaskGroup turini taklif etadi:

  1. withTaskGroup - Ichidagi vazifalar hech qanday xatolik (error) qaytarmaydigan bo'lsa.
  2. withThrowingTaskGroup - Ichidagi funksiyalardan ba'zilari throw orqali xatolik qaytarishi mumkin bo'lsa. Biz serverdan ma'lumot yuklayotganimiz uchun doimo asosan shundan foydalanamiz.

Kod namunasi: Rasm yuklovchi guruh

Endi tasavvur qiling, bizga bitta emas, o'nlab url manzillar massivi [String] berilgan. Barchasini birdaniga parallel yuklab, ularni rasmlar massiviga [UIImage] aylantiruvchi funksiya yozamiz.

func fetchImagesWithTaskGroup(urlStrings: [String]) async throws -> [UIImage] {

    // Biz `withThrowingTaskGroup` ni ochyapmiz, u guruhdan nima qaytishini so'raydi. (UIImage?)
    return try await withThrowingTaskGroup(of: UIImage?.self) { group in

        var images: [UIImage] = []

        // 🔥 Kichik samaradorlik (performance) siri:
        // Kompilyator qancha element qo'shilishini oldindan bilmasligi sababli, unga xotiradan
        // qancha joy ajratishni (reserve) buyuramiz.
        images.reserveCapacity(urlStrings.count)

        // 1-qadam: Bizdagi barcha URL larni aylanamiz va guruhga VAZIFA (Task) qo'shamiz
        for urlString in urlStrings {
            group.addTask {
                // Bu kod parallel tarzda hamma url'lar uchun birdaniga ishga tushib ketadi
                // try o'rniga try? ishlatilishi juda MUHIM!
                try? await self.fetchImage(urlString: urlString)
            }
        }

        // 2-qadam: Asinxron sikl (loop) yordamida barcha natijalarning qaytishini kutib,
        // bo'sh array (images) ga qo'shib boramiz
        for try await taskResult in group {
            if let image = taskResult {
                images.append(image)
            }
        }

        // Hamma natija kelib bo'lgandan so'ng qaytarib yuboramiz
        return images
    }
}

Muhim Jiharlar

  1. try? ning qutqaruvchi roli: withThrowingTaskGroup ning eng nozik nuqtasi bor. Agar siz 50 ta rasmni yuklashni boshlasangiz va ulardan atigi 1 tasi xato (Error) bilan tugasa (va bu yerni try? emas try bilan yozgan bo'lsangiz), butun guruh xato deb qabul qilinadi va barcha to'g'ri ishlagan yuklanishlar ham bekor qilinadi. Bunga yo'l qo'ymaslik uchun vazifalarning natijasini xavfsizroq (try? orqali optional qilib) belgiladik va qulab tushmaslik kafolatini oldik.
  2. Tartibsiz qaytish: Yuqoridagi loop ichidagi qismda (for try await taskResult in group) e'tibor bergan bo'lsangiz, kutish asinxrondir. Qaysi rasm birinchi yuklanib bo'lsa, xuddi o'sha tartibda array ga kiradi. Ular aynan birinchi url birinchi bo'lib qaytadi degan kafolat yo'q (serverning javobiga bog'liq).
  3. Meros (Inheritance): Siz qo'shgan har bir addTask ota vazifaning (Parent Task) barcha xususiyatlarini va ustuvorligini (priority) meros qilib oladi. Bu tizim resurslarini isrof qilmasligini ta'minlaydi.

Xulosa

  • Kichik, faqat 2-3 ta so'rov qilinayotgan hollarda kod yozilishini osonlashtirish uchun async let mukammal tanlov.
  • Lekin bir turdagi yuzlab massiv qiymatlarni tez va samarali ishlash kerak bo'lsa, unda faqat va faqat TaskGroup ishlatish tavsiya qilinadi! U ilovangiz tezligini bir necha barobarga oshirishi tayin.
Buy mea coffee