Generatorlar
Oddiy funksiya bir marta ishga tushib, bir marta qiymat qaytaradi va tugaydi. Generator esa boshqacha โ u bir necha marta "to'xtab", bir necha bor qiymat qaytara oladi va keyin qoldirilgan joyidan davom etadi. Generatorlar iteratorlar bilan birga ishlaganda ma'lumotlar oqimini juda oson yaratishga imkon beradi.
Generator funksiyasi
Generator yaratish uchun maxsus sintaksisdan foydalaniladi: function* (yulduzcha bilan). Bunday funksiyalar generator funksiyalari deb ataladi.
Yulduzchani function so'zidan keyin ham, funksiya nomidan oldin ham qo'yish mumkin โ function* gen() yoki function *gen(). Odatda birinchi ko'rinish afzal ko'riladi.
function* generateSequence() {
yield 1;
yield 2;
return 3;
}
Generator funksiyasining o'zi chaqirilganda kod bajarilmaydi. Uning o'rniga u maxsus generator obyektini qaytaradi. Bu obyekt bajarilishni boshqarishga xizmat qiladi.
next() chaqirilganda bajarila boshlaydi.yield โ asosiy metod
Generatorning yuragi โ yield operatori. Aynan u generatorni oddiy funksiyadan ajratib turadi. yield ma'nosi: "bu yerda to'xta, qiymat qaytar va meni kutib tur".
Generator ishlashi uchun uning next() metodi chaqirilishi kerak. next() chaqirilganda kod eng yaqin yield <qiymat> gacha bajariladi (qiymat tashlab yuborilishi mumkin, u holda undefined bo'ladi). So'ng bajarilish to'xtaydi va qiymat tashqariga qaytariladi.
next() ning natijasi โ doim ikkita xususiyatli obyekt:
valueโ qaytarilgan qiymat;doneโ agar generator kodi tugagan bo'lsatrue, aks holdafalse.
Diqqat qiling: birinchi next() birinchi yield 1 gacha bajarildi va {value: 1, done: false} qaytardi. Ikkinchisi yield 2 gacha davom etib, {value: 2, done: false} qaytardi. Uchinchisi return 3 gacha yetib bordi โ bu generatorning tugashini bildiradi, shuning uchun done: true.
done: true), keyingi next() chaqiruvlari doim {value: undefined, done: true} qaytaradi. Tugagan generatorni qayta ishga tushirib bo'lmaydi โ buning uchun yangi generator obyekti yaratish kerak.yield va return orasidagi farq: yield to'xtatadi lekin generator hali done emas; return esa generatorni butunlay tugatadi. Shuning uchun for..of return qiymatini e'tiborsiz qoldiradi (buni pastda ko'ramiz).Generatorlar โ iterativlar (iterable)
Generator obyektlari iterativ (iterable) hisoblanadi. Bu degani, ularni for..of sikli bilan aylanib chiqish mumkin:
Bu next() ni qo'lda chaqirishdan ancha chiroyliroq ko'rinadi, shunday emasmi?
for..of iteratsiyasi done: true bo'lgan oxirgi qiymatni e'tiborsiz qoldiradi. Shuning uchun agar barcha qiymatlar for..of orqali ko'rinishini xohlasangiz, ularni return emas, yield orqali qaytaring.Quyidagi misolda farqni ko'ring โ return 3 ishlatilganda 3 ko'rinmaydi:
Spread sintaksisi bilan
Generatorlar iterativ bo'lgani uchun, spread sintaksisi ... ham ular bilan ishlaydi. Bu generator natijalarini massivga aylantirishning qulay usuli:
Map, Set ishlaydigan har qanday joyda generatorlarni ham ishlata olasiz โ chunki ularning barchasi umumiy iteratsiya protokoliga bo'ysunadi.Iterativlar o'rniga generatorlardan foydalanish
Ilgari (masalan, iteratorlar mavzusida) biz range obyektiga Symbol.iterator qo'shib, uni iterativ qilgan bo'lishimiz mumkin. Generatorlar bu ishni ancha soddalashtiradi. Quyida โ generatorli, sodda va o'qishga oson variant:
Bu ishlaydi, chunki range[Symbol.iterator]() endi generatorni qaytaradi, generator obyektida esa next() metodi va [Symbol.iterator] allaqachon mavjud. Ya'ni generator obyektining o'zi ham iterator, ham iterativ.
*[Symbol.iterator]() { ... } โ bu obyekt metodini generator sifatida e'lon qilishning qisqa yozuvi. Yulduzcha (*) metodni generatorga aylantiradi.Cheksiz generatorlar
Generatorlar dangasa (lazy) โ ular qiymatni faqat so'ralganda ishlab chiqaradi. Shu sabab, cheksiz ketma-ketliklarni ham yaratish mumkin. Muhimi โ bunday generatorni cheksiz aylantirmang, aks holda dastur qotib qoladi.
for..of yoki [...gen] bilan to'liq aylanib chiqmang โ bu abadiy sikl va dastur to'xtab qolishiga olib keladi. Ulardan doim next() orqali, kerakli miqdorda qiymat oling.Generator kompozitsiyasi: yield*
Generator kompozitsiyasi โ bir generatorni boshqasining ichiga "joylash" (embed qilish) imkoniyati. Buning uchun maxsus yield* sintaksisidan foydalaniladi.
Misol uchun, ketma-ket sonlar generatorimiz bo'lsin, keyin uni raqamlar va harflar ketma-ketligini yaratish uchun qayta ishlataylik:
yield* gen ifodasi gen generatorga delegatsiya qiladi: uning barcha yield lari xuddi tashqi generatorning o'zidan chiqqandek tashqariga uzatiladi. Natija xuddi ichki generatorning kodi tashqi generator ichiga to'g'ridan-to'g'ri yozilgandek bo'ladi.
Quyidagi ikki variant bir xil natija beradi:
yield* istalgan iterativ bilan ishlaydi โ nafaqat generatorlar, balki massivlar va stringlar bilan ham. Masalan, yield* [1, 2, 3] yoki yield* 'abc' ham to'g'ri ishlaydi.next(qiymat) orqali ma'lumot uzatish
Hozirgacha generatorlar ma'lumot manbaiga o'xshardi โ ular qiymat chiqarardi. Ammo generatorlar ikki tomonlama aloqa quroli: ularga ichkariga ham qiymat uzatish mumkin. Buning uchun generator.next(qiymat) ga argument beriladi.
Bu qiymat yield ifodasining natijasi bo'lib qaytadi. Ya'ni let result = yield ... yozganda, result ga aynan keyingi next(qiymat) dagi qiymat tushadi.
Bu jarayonni bosqichma-bosqich tushunish muhim:
- Birinchi
next()doim argumentsiz chaqiriladi (unga uzatilgan qiymat e'tiborsiz qoldiriladi), chunki hali kutayotganyieldyo'q. U kodni birinchiyieldgacha yuguradi. - Keyin
next(4)chaqiriladi โ 4 qiymati generatorga qaytadi vayieldifodasining natijasi bo'lib,javobo'zgaruvchisiga tushadi. - Generator keyingi
yieldgacha (yoki oxirigacha) davom etadi.
Ko'p savolli, murakkabroq misol:
next(qiymat) ni tushunishning eng oson yo'li: yield โ bu "ikki tomonli darvoza". Undan chiqadigan qiymat yield dan keyin yoziladi, unga kiradigan qiymat esa keyingi next() dan keladi.generator.return va generator.throw
Generatorlarda next() dan tashqari yana ikki metod bor:
generator.return(qiymat)โ generatorni majburan tugatadi va berilgan qiymatni qaytaradi;generator.throw(xato)โ generator ichida, joriyyieldo'rnida xatolik (exception) chiqaradi.
return() chaqirilgach generator tugaydi โ keyingi barcha next() lar {value: undefined, done: true} qaytaradi.Amaliy qo'llanishi
Generatorlar aynan qaysi vaziyatlarda foydali?
- Ketma-ketliklar โ sonlar oqimi, ID generatorlar, sahifalash (pagination) kabi cheksiz yoki katta ma'lumot oqimlarini dangasa hosil qilish;
- Maxsus iterativlar โ o'z obyektlaringizni
for..ofbilan ishlaydigan qilish (yuqoridagirangemisoli); - Murakkab tuzilmalarni tekislash โ daraxtsimon (tree) tuzilmalarni
yield*orqali oson rekursiv aylanib chiqish; - Holatni saqlash โ generator o'z bajarilish holatini (o'zgaruvchilar, joyi) o'zida saqlaydi, bu ba'zi vazifalarni soddalashtiradi.
Quyida โ noyob ID generator, amaliy va foydali namuna:
Yana bir misol โ daraxtsimon tuzilmani yield* rekursiya bilan tekislash:
Xulosa
- Generatorlar
function*orqali yaratiladi va chaqirilganda generator obyekti qaytaradi (kod darhol bajarilmaydi); yieldgeneratorni to'xtatadi va qiymat qaytaradi;next()uni davom ettiradi;next()natijasi โ{value, done}obyekti;- Generatorlar iterativ โ ular
for..of, spread...bilan ishlaydi (lekinreturnqiymatifor..ofda ko'rinmaydi); yield*boshqa generator yoki iterativga delegatsiya qiladi;next(qiymat)generatorning ichiga qiymat uzatadi โ buyieldifodasining natijasi bo'ladi;- Generatorlar dangasa hisoblanadi โ cheksiz ketma-ketliklar, ID generatorlar, maxsus iterativlar uchun ideal.