- Published on
Continuation yordamida eski kodlarni Async'ga o'tkazish
- Authors
- Name
- ShoxruxC
- @iOSdasturchi
Nima uchun Continuation kerak?
Shu paytgacha darslarimizda biz asosan Apple o'zining tayyorlab qo'ygan URLSession.shared.data(from:) asinxron xususiyatidan foydalandik. U allaqachon async/await uchun moslashtirilgan.
Lekin amalda, ilovalar yaratishda juda ko'p uchinchi tomon kutubxonalaridan (SDK) yoki hali yangilanmagan eski bazaviy kodlardan foydalanamiz. Ular odatda ma'lumotni @escaping closure'lar, ya'ni completion handlerlar yordamida qaytaradi. Katta loyihalarda faqatgina 1 ta eski funksiya uchun butun ilova muhitini asinxrondan voz kechishga majburlay olmaymiz. Bunga yechim sifatida, eskirgan funksiyani "zamonashtiradigan" mexanizm mavjud va u Continuation deb ataladi.
Continuation turlari
Sizda funksiyaning turiga qarab asosan 2 ta tanlov bo'ladi:
withCheckedContinuation– Hech qanday xatolik (Error) qaytarmaydigan oddiy operatsiyalar uchun.withCheckedThrowingContinuation– Xatolik yuzaga kelishi mumkin bo'lgan operatsiyalar uchun. (Kodni ko'proq shu turda yozamiz)
Apple shuningdek, "Unsafe" (xavfsiz bo'lmagan) continuation turlarini ham qo'shgan. Lekin ulardan foydalanish deyarli tavsiya etilmaydi, chunki ular faqatgina kichik samaradorlik (performance) uchun mo'ljallangan va xatolarni tizim qattiq tekshirmaydi. Biz doimo "Checked" turlaridan foydalanamiz.
Eski kodni modernizatsiya qilish misoli
Faraz qilaylik, bizda rasmni ma'lumotlar bazasidan oluvchi va completion handler ishlatadigan eski funksiya bor:
// Eski (SDK) usulidagi funksiya:
func getHeartImageFromDatabase(completionHandler: @escaping (UIImage?, Error?) -> Void) {
// 5 soniyalik kechikish simulyatsiyasi
DispatchQueue.main.asyncAfter(deadline: .now() + 5) {
// Vaqt o'tgach ma'lumot jo'natiladi
completionHandler(UIImage(systemName: "heart.fill"), nil)
}
}
Buni qanday qilib ajoyib async funksiyaga aylantirishimiz mumkin? withCheckedThrowingContinuation bloki yordamida!
// Yangilangan (Async/Await) ko'rinishi
func getHeartImageAsync() async throws -> UIImage {
// Asinxron muhitga Continuation orqali ulanamiz
return try await withCheckedThrowingContinuation { continuation in
// Asl (eski) funksiyani ichkarida chaqiramiz
getHeartImageFromDatabase { image, error in
// Agar rasm olsak, uni qaytaramiz (resume returning)
if let image = image {
continuation.resume(returning: image)
return
}
// Agar xatolik bo'lsa, xatoni uloqtiramiz (resume throwing)
if let error = error {
continuation.resume(throwing: error)
return
}
// Hamma holat inobatga olinganiga ishonch hosil qilish uchun qo'shimcha xato
continuation.resume(throwing: URLError(.badURL))
}
}
}
Endi ViewModel ichida biz ushbu toza va muammosiz ishlaydigan yangi funksiyadan osongina foydalanishimiz mumkin bo'ladi:
Task {
do {
let image = try await getHeartImageAsync()
self.image = image
} catch {
print(error)
}
}
🚨 OLTIN QOIDA: Faqat va Faqat BIR marta!
Apple'ning rasmiy hujjatlarida shunday deyilgan: "Siz Continuation.resume funksiyasini AYNAN BIR MARTA (exactly once) chaqirishingiz shart!"
Buning ma'nosi nima?
- Nol marta (chqirilmasa): Tasavvur qiling, kod yuqoridagi
ifbloklariga tushmadi va oxirgi uchinchiresumeqatori yozilmagan. Natijadaresumeumuman chaqirilmay qoladi. Bu holda dastur bu Task'ni "Tugadi" deb o'ylamaydi va umrbod xotirada qotib qoladi (Memory Leak)! - Ikki marta (yoki undan ko'p): Tasavvur qiling, siz tasodifan hamma joyga
resumeyozib tashladingiz yokireturnso'zini esdan chiqardingiz. Agar bitta Continuation ichida ikki martaresumeishlansa, dastur (App) to'g'ridan-to'g'ri CRASH bo'ladi (qulaydi). Tizim buni darhol fosh qiladi.
Shuning uchun, Continuation yozayotganingizda doimo barcha mantiqiy yo'llar (if, else, switch) oxir-oqibat faqatgina bitta o'z vaqtida chqiriladigan resume ga borib ulanishiga 100% ishonch hosil qiling!