Published on

Async Let yordamida kodlarni parallel ishlash

Authors

Asinxron kodlarning ketma-ketlik muammosi

O'tgan darslarda ko'rganimizdek, agar bitta Task ichida bir nechta await chaqiruvlari ketma-ket yozilsa, ular birin-ketin (sinxrondek) ishlaydi. Ya'ni birinchi amal butunlay tugamagunicha, ikkinchi amal boshlanmaydi.

Tasavvur qiling, biz internetdan 4 xil rasmni yuklab olmoqchimiz:

Task {
    do {
        // Har biri 2 soniyadan vaqt oladi deylik. Jami: 8 soniya kutiladi.
        let image1 = try await fetchImage()
        let image2 = try await fetchImage()
        let image3 = try await fetchImage()
        let image4 = try await fetchImage()

        self.images.append(contentsOf: [image1, image2, image3, image4])
    } catch { ... }
}

Albatta, 4 ta rasmni alohida-alohida Task qilib ochish mumkin, lekin bunday qilsak kodimiz juda xunuklashadi va agar ekrandan chiqib ketsak bu Tasklarni bitta-bitta to'xtatib (cancel qilib) chiqishimiz kerak bo'ladi. Xo'sh, bitta Task ichida bu jarayonni qanday qilib baravariga (parallel) bajarish mumkin?

Async Let — Parallellik yechimi

Swift bizga async let degan ajoyib xususiyatni taqdim etadi. Bu xususiyat asinxron funksiyani kutmasdan darhol boshlab yuborish imkonini beradi. Natijani esa kerak bo'lganda birgalikda kutib olamiz.

Task {
    do {
        // Bu kodlar funksiyani darhol ishga tushiradi, lekin natijasini kutib o'tirmaydi!
        async let fetchImage1 = fetchImage()
        async let fetchImage2 = fetchImage()
        async let fetchImage3 = fetchImage()
        async let fetchImage4 = fetchImage()

        // Hamma funksiyalar allaqachon bir vaqtda (parallel) ishni boshlagan.
        // Endi biz ularning barchasi tayyor bo'lishini bitta qatorda kutib (await) olamiz:
        let (image1, image2, image3, image4) = await (try fetchImage1, try fetchImage2, try fetchImage3, try fetchImage4)

        // Agar har bir rasm yuklanishi 2 soniyadan olsa, endi jami operatsiya atigi 2 soniya davom etadi!
        self.images.append(contentsOf: [image1, image2, image3, image4])
    } catch {
        print("Xatolik: \(error)")
    }
}

Muhim qoidalar va afzalliklari

  1. Xatolar (Errors): Yuqoridagi misolda funksiya xato (error) qaytarishi mumkin bo'lganligi sababli biz guruh ichida ham try so'zidan foydalandik. Esda tuting: Agar shu to'rtta rasmning bittasida xatolik chiqsa (try qulasa), butun guruh bekor qilinadi va kod darhol catch blokiga tushib ketadi. Bunga yo'l qo'ymaslik uchun, ma'lumot tushib qolishi mumkin bo'lgan qismlarda try? ishlating.
  2. Bir xil bo'lishi shart emas: async let bilan turli xil qiymat qaytaruvchi mutlaqo boshqa-boshqa funksiyalarni ham birga chaqirish mumkin.

Masalan:

async let fetchImage1 = fetchImage() // UIImage qaytaradi (throws qiladi)
async let fetchTitle1 = fetchTitle() // String qaytaradi (throws qilmaydi)

// E'tibor bering, fetchTitle throws qilmagani uchun, uning oldida try shart emas.
let (image, title) = await (try fetchImage1, fetchTitle1)

Qachon async let ishlatmaslik kerak?

async let barmoq bilan sanarli (masalan 3-4 ta) so'rovlarni bir vaqtda bajarish uchun ideal vositadir. Ya'ni, profil sahifasiga kirganda 1 marta foydalanuvchi ma'lumoti, 1 marta uning do'stlari ro'yxati va 1 marta rasmini paralel chaqirish kerak bo'lsa — bu ayni muddao.

Biroq, agar sizda array ko'rinishidagi 50 ta rasm bo'lsa va siz ularning hammasini bitta-bitta paralel yuklamoqchi bo'lsangiz, uni bunday yozib bo'lmaydi: async let image50 = fetchImage50()

Buning uchun yanada kuchliroq bo'lgan TaskGroup tushunchasi mavjud. U haqida keyingi darsimizda gaplashamiz.

Buy mea coffee