Mundarija (34)
- Bu darsda
- 1. Nega bu kerak?
- 2. Birinchi generator
- 2.1 function* — chaqirilganda hech narsa bajarilmaydi
- 2.2 next() — "davom" tugmasi
- 2.3 Qadamma-qadam
- 2.4 return — oxirgi qiymat
- 3. Generator — tayyor iterator
- 3.1 Protokolning hamma joyida ishlaydi
- 3.2 Generator obyekti — bir martalik, generator funksiya — fabrika
- 3.3 oraliq — 15 qatordan 4 qatorga
- 3.4 Generator metod: obyekt va klassda
- 4. Dangasa hisob: qiymat so'ralganda tug'iladi
- 4.1 Oshxona va ofitsiant
- 4.2 Qachon foydali?
- 5. Cheksiz generator
- 5.1 Navbat chiptalari apparati
- 5.2 Cheksizni xavfsiz ishlatish
- 5.3 Navbatchilar aylanasi
- 6. "Generator" so'zi — ikki ma'noda
- 7. Ko'p uchraydigan xatolar
- 7.1 Yulduzchani unutish
- 7.2 yield callback ichida
- 7.3 Chaqirdim — hech narsa bo'lmadi
- 7.4 Tugagan generatorni qayta aylanish
- 7.5 return qiymatini for...of dan kutish
- 8. Mashqlar
- 1-mashq (oson): Ish kunlari generatori
- 2-mashq (o'rta): Sahifalar — generator bilan
- 3-mashq (qiyin): Birinchi N tasi — cheksiz bilan
- 4-mashq: Vazifalar qadami — id yasovchi: closure va generator (mashqlar repo)
- 9. Real ishda
- Xulosa
- Manbalar
JavaScript generator funksiyalar: function* va yield bilan to'xtab-davom etish
Qisqacha: Generator funksiya (
function*) chaqirilganda tanasini darhol bajarmaydi — generator obyektini qaytaradi. Harnext()tanani keyingiyieldgacha bajaradi,yielddagi qiymatni{ value, done: false }qilib beradi va funksiyani shu joyda to'xtatib qo'yadi. Keyinginext()aynan shu joydan davom ettiradi. Generator — tayyor iterator: o'tgan darsdagi 15 qatorli qo'lda yozilgan iterator generator bilan 3–4 qatorga tushadi.
Bu darsda
function*vayieldbilan generator yozasiz vanext()qanday "to'xtatib-davom ettirishini" kuzatasiz.- Generator iteratsiya protokoliga to'liq amal qilishini —
for...of, spread, destructuring bilan ishlashini ko'rasiz. - O'tgan darsdagi
oraliqva klass iteratorini generator bilan qayta yozasiz. - Dangasa (lazy) va cheksiz ketma-ketliklarni yasaysiz va ularni xavfsiz ishlatasiz.
yieldqayerda ishlamasligini vaUnexpected strict mode reserved wordxatosini bilasiz.
Oldin bilishingiz kerak: Iteratsiya protokoli, O'z iterable obyektingizni yasash, Closure amaliyotda, Strict mode.
1. Nega bu kerak?
O'tgan darsda oraliq fabrikasini yozdik. Ishlaydi, lekin unga qarang: tashqi obyekt, [Symbol.iterator]() metodi, ichki obyekt, next(), joriy ni qo'lda surish, { value, done } ni qo'lda yasash. Mohiyati bitta jumla — "boshidan oxirigacha qadam bilan sanab ber" — lekin 15 qatorga cho'zildi. Holatni boshqarish ham bizning zimmamizda: joriy ni oshirishni yoki done: true ni unutsak — xato yoki cheksiz sikl.
Oddiy funksiya bilan buni yozish qanchalik oson bo'lardi:
function oraliqniChiqar(boshi, oxiri) {
for (let i = boshi; i < oxiri; i++) {
console.log(i);
}
}
oraliqniChiqar(10, 13);Konsolda:
10
11
12Lekin bu funksiya sonlarni o'zi chiqaradi — ularni qaytarib berolmaydi. return bir marta ishlaydi va funksiya tugaydi. Bizga esa "bitta son ber, to'xtab tur, yana so'rasam — keyingisini ber" kerak.
Hayotdan o'xshatish: video ko'ryapsiz. Pauza ni bossangiz, video aynan shu kadrda to'xtaydi. Hech narsa unutilmaydi — qahramon qayerda turgan bo'lsa, o'sha yerda. Davom ni bossangiz, video o'sha kadrdan davom etadi, boshidan emas. Generator — pauza tugmasi bor funksiya. yield — pauza, next() — davom.
2. Birinchi generator
2.1 function* — chaqirilganda hech narsa bajarilmaydi
function* taomlar() {
console.log("Generator boshlandi");
yield "osh";
yield "manti";
}
const g = taomlar();
console.log("Funksiya chaqirildi");
console.log(g);Konsolda:
Funksiya chaqirildi
Object [Generator] {}Diqqat qiling: "Generator boshlandi" chiqmadi. Oddiy funksiyada taomlar() darhol tanani bajarardi. Generator funksiya esa tanasiga tegmay, generator obyektini qaytardi — xuddi yangi qo'yilgan, hali "Ijro" bosilmagan video kabi.
Ikki yangi narsa:
- Generator funksiya (generator function) —
functiondan keyin yulduzcha (*) qo'yilgan funksiya. U chaqirilganda tanasini emas, generator obyektini qaytaradi. yield(inglizcha "bermoq, topshirmoq") — faqat generator funksiya ichida ishlaydigan kalit so'z. U qiymatni tashqariga beradi va funksiyani shu joyda to'xtatadi.
Yulduzcha function*, function * yoki function * taomlar deb yozilishi mumkin — barchasi bir xil. Odatda function* yoziladi: "bu funksiya turi" ekani ko'zga darrov tashlanadi.
2.2 next() — "davom" tugmasi
Endi generatordan qiymat so'raymiz:
console.log(g.next());
console.log(g.next());
console.log(g.next());Konsolda:
Generator boshlandi
{ value: 'osh', done: false }
{ value: 'manti', done: false }
{ value: undefined, done: true }Tanish shakl — { value, done }! Bu iteratsiya protokolidagi iterator natijasining o'zi. Generator — iterator, va bu natijani biz yozmadik: JavaScript yield dagi qiymatdan o'zi yasadi.
Har next() nima qildi:
- 1-
next()— tana boshidan ishga tushdi: "Generator boshlandi" chiqdi, birinchiyield "osh"ga yetdi."osh"tashqariga berildi, funksiya to'xtadi. - 2-
next()— to'xtagan joydan,yield "osh"dan keyingi qatordan davom etdi. Ikkinchiyieldga yetdi —"manti"berildi, yana to'xtadi. - 3-
next()— davom etdi, lekinyieldqolmadi: tana oxirigacha bajarildi. Funksiya tugadi —done: true.
2.3 Qadamma-qadam
Keyingi ni bosing va funksiya qayerda to'xtab, qayerdan davom etishini kuzating. Stek ustuniga ham qarang: generator to'xtaganda u stekdan chiqib ketadi, lekin o'lmaydi.
Bu — generatorning siri. Oddiy funksiya stekdan chiqsa, uning lokal o'zgaruvchilari yo'qoladi (Execution context va call stack). Generator esa to'xtaganda butun holatini — o'zgaruvchilari va "qaysi qatordaman" degan joyini — generator obyektida saqlab qo'yadi. next() uni qaytadan stekka chiqaradi.
Generator hayoti davomida to'rt holatdan o'tadi. Diagrammada strelkalar — nima uni bir holatdan boshqasiga o'tkazishi:
stateDiagram-v2
[*] --> Boshlanmagan: taomlar()
Boshlanmagan --> Ishlayapti: next()
Ishlayapti --> Toxtagan: yield
Toxtagan --> Ishlayapti: next()
Ishlayapti --> Tugagan: tana tugadi / return
Tugagan --> Tugagan: next() → done trueIkki narsani eslab qoling. Birinchidan, "Ishlayapti" holatiga faqat next() olib kiradi — generator o'zidan o'zi hech qachon davom etmaydi. Ikkinchidan, "Tugagan" holatdan chiqish yo'q: tugagan generatorni qayta boshlab bo'lmaydi, faqat generator funksiyani yana chaqirib, yangi generator olish mumkin.
2.4 return — oxirgi qiymat
Generatorda return ham yozish mumkin:
function* buyurtma() {
yield "osh";
yield "choy";
return "hisob: 40000";
}
const hisob = buyurtma();
console.log(hisob.next()); // { value: 'osh', done: false }
console.log(hisob.next()); // { value: 'choy', done: false }
console.log(hisob.next()); // { value: 'hisob: 40000', done: true }
console.log([...buyurtma()]); // [ 'osh', 'choy' ]return dagi qiymat done: true bilan birga keladi. Lekin for...of va spread uni tashlab yuboradi: ular done: true ni ko'rishi bilan to'xtaydi va value ga qaramaydi (Iteratsiya protokoli darsidagi qadamlarni eslang). Spread natijasida hisob yo'q. Qoida: tashqariga beriladigan har bir qiymat yield bilan. return qiymati faqat next() ni qo'lda chaqirganda va keyingi darsdagi yield* da ko'rinadi.
Tekshirib ko'ring: Generator funksiyada uchta
yieldbor.for...ofuni oxirigacha aylangandanext()necha marta chaqiriladi? Generator tanasining oxirgiyielddan keyingi qatorlari bajariladimi?
Javob
To'rt marta: uchtasi qiymat uchun, to'rtinchisi done: true uchun. Ha, bajariladi — aynan to'rtinchi next() generatorni oxirgi yield dan davom ettiradi va tanani oxirigacha bajaradi. Shundan keyingina done: true keladi. break bilan erta chiqilsa esa bu qatorlar bajarilmaydi.
3. Generator — tayyor iterator
3.1 Protokolning hamma joyida ishlaydi
Generator obyekti ham iterator (next() bor), ham iterable (o'zini qaytaradigan [Symbol.iterator]() bor):
function* bahorTaomlari() {
yield "osh";
yield "lag'mon";
yield "manti";
}
const gen = bahorTaomlari();
console.log(gen[Symbol.iterator]() === gen); // true
for (const taom of bahorTaomlari()) {
console.log(taom);
}
const [birinchi] = bahorTaomlari();
console.log(birinchi); // osh
console.log(Array.from(bahorTaomlari(), (t) => t.toUpperCase()));Konsolda:
true
osh
lag'mon
manti
osh
[ 'OSH', "LAG'MON", 'MANTI' ]O'tgan darsdagi tekshiruv ro'yxatini eslang: yangi iterator, { value, done }, done: true albatta keladi, iterator o'zi ham iterable. Generator bularning hammasini avtomatik bajaradi. Hatto return() metodi ham bor — break da u ham o'z-o'zidan ishlaydi.
3.2 Generator obyekti — bir martalik, generator funksiya — fabrika
const bitta = bahorTaomlari();
console.log([...bitta].length); // 3
console.log([...bitta].length); // 0
console.log([...bahorTaomlari()].length); // 3Generator obyekti — xatcho'p: bir marta o'qiladi (O'z iterable obyektingizni yasash darsidagi "bir martalik" dizayn). Generator funksiya esa — xatcho'p zavodi: har chaqiruv yangi, boshidan boshlanadigan generator beradi. Shuning uchun generatorni o'zgaruvchiga saqlab, ikki joyda aylanmang — har safar funksiyani qayta chaqiring.
3.3 oraliq — 15 qatordan 4 qatorga
O'tgan darsdagi fabrikani generator bilan yozamiz:
function* oraliq(boshi, oxiri, qadam = 1) {
if (qadam <= 0) return;
for (let i = boshi; i < oxiri; i += qadam) {
yield i;
}
}
console.log([...oraliq(10, 13)]); // [ 10, 11, 12 ]
console.log([...oraliq(2, 11, 2)]); // [ 2, 4, 6, 8, 10 ]
console.log([...oraliq(1, 5, 0)]); // []Qo'lda yozilgan variantdagi hamma narsa joyida, faqat ko'rinmas bo'lib qoldi:
| Qo'lda iterator | Generator |
|---|---|
let joriy closure'da |
let i — oddiy lokal o'zgaruvchi |
next() ichida if |
oddiy for sikli |
{ value: x, done: false } |
yield x |
{ value: undefined, done: true } |
funksiya tugashi (yoki return) |
Holat endi oddiy sikl o'zgaruvchisi: uni JavaScript generator to'xtaganda o'zi saqlaydi. if (qadam <= 0) return; — erta chiqish: generator darhol tugaydi, iterable bo'sh.
O'zingiz to'ldiring. [...oraliq(0, 3)] natijasi — [0, 1, ]. oraliq(0, 3) generatoridan next() ni to'rtinchi marta chaqirsak, done — .
3.4 Generator metod: obyekt va klassda
Metod ham generator bo'la oladi — nomidan oldin yulduzcha. Eng ko'p uchraydigan joyi — [Symbol.iterator]:
class Menyu {
#taomlar = [];
qosh(nom, narx) {
this.#taomlar.push({ nom, narx });
}
*[Symbol.iterator]() {
for (const taom of this.#taomlar) {
yield taom.nom;
}
}
}
const menyu = new Menyu();
menyu.qosh("Osh", 35000);
menyu.qosh("Manti", 30000);
console.log([...menyu]); // [ 'Osh', 'Manti' ]O'tgan darsda Menyu iteratori 15 qator edi, this uchun closure o'zgaruvchisi ham kerak bo'lgandi. Bu yerda this to'g'ri ishlaydi: generator metod oddiy metod kabi menyu da chaqiriladi, uning tanasi esa next() da davom etganda ham o'sha this ni eslaydi. Qo'shimcha foyda — iterator nimani berishini o'zgartirish oson: bu yerda butun obyekt emas, faqat nom berildi.
Generator yozishning hamma shakllari:
| Joy | Yozuv |
|---|---|
| E'lon | function* nom() {} |
| Ifoda | const nom = function* () {}; |
| Obyekt metodi | { *nom() {} }, { *[Symbol.iterator]() {} } |
| Klass metodi | class K { *nom() {} } |
Arrow generator yo'q. *() => {} — sintaksis xatosi (SyntaxError: Unexpected token '*'). Generator new bilan ham chaqirilmaydi: new taomlar() — TypeError: taomlar is not a constructor.
Tekshirib ko'ring:
Menyudagi generator metodni yulduzchasiz — oddiy[Symbol.iterator]() { ... yield taom.nom; }deb yozsak, nima bo'ladi?
Javob
Fayl umuman ishga tushmaydi — sintaksis xatosi. Yulduzchasiz metod oddiy funksiya, uning ichida yield kalit so'z emas. Klass tanasi esa doim qat'iy rejimda (Strict mode) — u yerda yield ni oddiy nom sifatida ham ishlatib bo'lmaydi. Xabar: SyntaxError: Unexpected strict mode reserved word. Tafsiloti — «Ko'p uchraydigan xatolar» bo'limida.
4. Dangasa hisob: qiymat so'ralganda tug'iladi
4.1 Oshxona va ofitsiant
Generator qiymatni oldindan hisoblamaydi — faqat next() so'raganda. Buni ko'rish uchun ikki tomonni gapirtiramiz: oshxona (generator) va ofitsiant (for...of):
function* oshxona() {
for (const taom of ["osh", "manti", "somsa"]) {
console.log(`Oshxona: ${taom} tayyorlanmoqda`);
yield taom;
}
}
for (const taom of oshxona()) {
console.log(`Ofitsiant: ${taom} stolga`);
if (taom === "manti") break;
}Konsolda:
Oshxona: osh tayyorlanmoqda
Ofitsiant: osh stolga
Oshxona: manti tayyorlanmoqda
Ofitsiant: manti stolgaIkki narsaga qarang:
- Navbatma-navbat. Oshxona barcha taomni avval tayyorlab, keyin bermadi. Bitta tayyorladi — ofitsiant olib ketdi — keyin keyingisi. Massiv bilan ishlaganda hamma qiymat oldindan tayyor bo'lardi.
- Somsa umuman tayyorlanmadi. Ofitsiant
breakqildi — oshxonaga boshqa buyurtma kelmadi. Generator 2-taomdan keyin to'xtagan holda qoldi va tashlab yuborildi.
Bu uslub dangasa hisob (lazy evaluation) deb ataladi: qiymat faqat kerak bo'lganda va faqat kerakli miqdorda hisoblanadi. Uning teskarisi — "g'ayratli" hisob (eager evaluation): massiv metodlari (map, filter) hamma elementni darhol ishlaydi.
4.2 Qachon foydali?
- Qiymat qimmat bo'lsa. Har biri uchun serverdan so'rov yoki og'ir hisob kerak — faqat kerakligi hisoblanadi.
- Hammasi kerak bo'lmasa. "Birinchi mos keladigani" topilishi bilan to'xtash.
- Ketma-ketlik cheksiz bo'lsa. Massiv cheksiz bo'lolmaydi, generator — bo'la oladi.
Dangasalikning narxi ham bor. Generatordan olingan qiymatlarni qayta ko'rish uchun ularni yana hisoblash kerak — massiv kabi "tayyor turgan" nusxa yo'q. Shuning uchun kichik, qayta-qayta o'qiladigan ro'yxatlar uchun oddiy massiv qulayroq. Generator — katta, qimmat yoki cheksiz ketma-ketliklar uchun. Qaysi birini tanlashni Iterator helpers darsida jadval bilan solishtiramiz.
Tekshirib ko'ring:
oshxona()dagifor...ofdanbreakni olib tashlasak, konsolda nechta qator bo'ladi va qaysi tartibda?
Javob
Olti qator, navbatma-navbat: "Oshxona: osh…", "Ofitsiant: osh…", "Oshxona: manti…", "Ofitsiant: manti…", "Oshxona: somsa…", "Ofitsiant: somsa…". Endi somsa ham tayyorlanadi, chunki ofitsiant uni so'radi. Tartib baribir navbatma-navbat — dangasalik break ga bog'liq emas.
5. Cheksiz generator
5.1 Navbat chiptalari apparati
Iteratsiya protokoli darsida "navbat chiptalari apparati"ni tasavvur qilgan edik — u hech qachon "tugadi" demaydi. Generator bilan u uch qator:
function* chiptalar() {
let raqam = 1;
while (true) {
yield raqam++;
}
}
const apparat = chiptalar();
console.log(apparat.next().value); // 1
console.log(apparat.next().value); // 2
console.log(apparat.next().value); // 3while (true) — oddiy funksiyada bu dasturni qotiradigan cheksiz sikl. Generatorda esa xavfsiz: har aylanishda yield funksiyani to'xtatadi. Sikl faqat kimdir next() so'raganda bir qadam aylanadi. Protsessor bo'sh turadi.
5.2 Cheksizni xavfsiz ishlatish
Cheksiz generatorni kerakligicha so'raydigan yo'llar bilan ishlating:
const [a, b, c] = chiptalar();
console.log(a, b, c); // 1 2 3
for (const raqam of chiptalar()) {
if (raqam > 2) break;
console.log(`Chipta: ${raqam}`);
}Konsolda:
1 2 3
Chipta: 1
Chipta: 2Destructuring uch marta so'radi. for...of dagi break siklni to'xtatdi. Har ikkala holatda generator o'zi to'xtamadi — uni iste'molchi (next() ni chaqiruvchi tomon) to'xtatdi.
Diqqat: Cheksiz generator bilan
[...chiptalar()],Array.from(chiptalar())yokibreaksizfor...of— dastur abadiy ishlaydi va xotira tugaguncha massiv yig'adi. TerminaldaCtrl+Cbilan to'xtating. Bunday generatorga nomida yoki izohida "cheksiz" deb belgi qo'ying.
5.3 Navbatchilar aylanasi
Cheksiz generatorning amaliy misoli — davriy navbat. «Bahor»da kechki navbatchilik har kuni almashadi:
function* aylanma(royxat) {
while (true) {
for (const kishi of royxat) {
yield kishi;
}
}
}
const navbatchilar = ["Otabek", "Rustam aka", "Jasur aka"];
const [du, se, chor, pay] = aylanma(navbatchilar);
console.log(du, se, chor, pay); // Otabek Rustam aka Jasur aka OtabekIchki for...of ro'yxatni bir marta aylanadi, tashqi while (true) uni qaytadan boshlaydi. To'rtinchi kun yana Otabek. Indeks va qoldiq (%) bilan hisoblash shart emas — generator "ro'yxat tugasa, boshidan" degan mantiqni oddiy sikl bilan ifodaladi.
Tekshirib ko'ring: Quyidagi generator qaysi ketma-ketlikni beradi?
const [a, b, c, d, e] = fib();dan keyinenimaga teng?
function* fib() { let [x, y] = [0, 1]; while (true) { yield x; [x, y] = [y, x + y]; } }
Javob
Fibonachchi sonlari: har son oldingi ikkitasining yig'indisi — 0, 1, 1, 2, 3, 5, 8… e — beshinchi qiymat, ya'ni 3. [x, y] = [y, x + y] — massiv destructuring bilan ikki o'zgaruvchini bir vaqtda yangilash. Ketma-ketlik cheksiz, lekin destructuring faqat besh marta so'radi. Bu — intervyularda generator bo'yicha eng ko'p beriladigan masala.
6. "Generator" so'zi — ikki ma'noda
Closure amaliyotda darsida idYasovchiYarat ni yasab, uni "id generatori" deb atagan edik. U generator funksiya emas — oddiy closure: har chaqiruvda yangi son qaytaradigan funksiya. Bu yerda "generator" — kundalik so'z: "ishlab chiqaruvchi, yasovchi".
Bugungi generator funksiya esa tilning maxsus turi: function*, yield, next(), iteratsiya protokoli. Kodni o'qiganda farqlash oson — yulduzchaga va yield ga qarang. Ikkalasi ham "keyingi qiymatni beradi", lekin har xil shaklda — buni «Vazifalar qadami» da solishtiramiz.
7. Ko'p uchraydigan xatolar
7.1 Yulduzchani unutish
function taomlar() {
yield "osh";
}Konsolda:
SyntaxError: Unexpected strict mode reserved wordTarjimasi: "qat'iy rejimda kutilmagan zaxiralangan so'z". Yulduzchasiz funksiyada yield kalit so'z emas. Qat'iy rejimda esa (Strict mode) yield — kelajak uchun zaxiralangan so'z: uni oddiy nom sifatida ham ishlatib bo'lmaydi. Natija — sintaksis xatosi, fayl umuman ishga tushmaydi. Modullar va klasslar doim qat'iy rejimda. Saytdagi «Ishga tushir» tugmasi ham kodni qat'iy rejimda bajaradi. Shuning uchun ko'pincha aynan shu xabarni ko'rasiz.
Oddiy (qat'iy bo'lmagan) skriptda xabar boshqacha: SyntaxError: Unexpected string — yield oddiy o'zgaruvchi nomi deb o'qiladi, undan keyingi "osh" esa "kutilmagan satr". Ikkala holatda ham xato ko'rsatgan so'z emas, uning atrofi muammo: oldingi so'zni — bu yerda yield ni — tekshiring. Tuzatish: function*.
7.2 yield callback ichida
function* taomlar(royxat) {
royxat.forEach((taom) => {
yield taom;
});
}Konsolda:
SyntaxError: Unexpected strict mode reserved wordXuddi o'sha xabar, chunki sabab bir xil: yield faqat generator funksiyaning o'z tanasida ishlaydi. Arrow callback — boshqa, oddiy funksiya, u generator emas. Tuzatish: forEach o'rniga for...of — sikl funksiya emas, u generatorning o'z tanasida qoladi:
function* taomlar(royxat) {
for (const taom of royxat) {
yield taom;
}
}
console.log([...taomlar(["osh", "manti"])]); // [ 'osh', 'manti' ]7.3 Chaqirdim — hech narsa bo'lmadi
taomlar(); deb yozib, konsolda hech narsa ko'rmaslik. Generator funksiya tanasini next() siz bajarmaydi. Oddiy funksiyadagi "chaqirildi — bajarildi" odati bu yerda ishlamaydi. Tuzatish: natijani for...of, spread yoki next() bilan iste'mol qiling.
7.4 Tugagan generatorni qayta aylanish
«Generator obyekti — bir martalik» bo'limidagi holat: const g = taomlar(); ni ikki marta spread qilsangiz, ikkinchisi []. Xato chiqmaydi. Tuzatish: generator funksiyani qayta chaqiring — [...taomlar()].
7.5 return qiymatini for...of dan kutish
return "hisob: 40000" dagi qiymat for...of da ko'rinmaydi («return — oxirgi qiymat» bo'limi). Tuzatish: tashqariga kerak bo'lgan hamma narsani yield bilan bering.
8. Mashqlar
1-mashq (oson): Ish kunlari generatori
O'tgan darsdagi ishKunlari ni generator funksiya bilan yozing: function* ishKunlari() dushanbadan shanbagacha olti kunni bersin. [...ishKunlari()].length — 6, const [birinchi] = ishKunlari(); — "Dushanba".
Yechim
function* ishKunlari() {
yield "Dushanba";
yield "Seshanba";
yield "Chorshanba";
yield "Payshanba";
yield "Juma";
yield "Shanba";
}
console.log([...ishKunlari()].length); // 6
const [birinchi] = ishKunlari();
console.log(birinchi); // DushanbaHar kun — alohida yield. Massiv va indeks kerak emas: generator qaysi yield da to'xtaganini o'zi eslaydi.
2-mashq (o'rta): Sahifalar — generator bilan
O'tgan darsdagi 3-mashq — sahifalar(massiv, hajm) — ni generator funksiya bilan qayta yozing. Massivni hajm talik bo'laklarga bo'lib bersin (slice, Kesish va birlashtirish), hajm 0 yoki manfiy bo'lsa — bo'sh.
const taomlar = ["Osh", "Lag'mon", "Manti", "Somsa", "Ko'k choy"];
console.log([...sahifalar(taomlar, 2)].length); // 3Yechim
function* sahifalar(massiv, hajm) {
if (hajm <= 0) return;
for (let i = 0; i < massiv.length; i += hajm) {
yield massiv.slice(i, i + hajm);
}
}
const taomlar = ["Osh", "Lag'mon", "Manti", "Somsa", "Ko'k choy"];
for (const sahifa of sahifalar(taomlar, 2)) {
console.log(sahifa);
}Konsolda:
[ 'Osh', "Lag'mon" ]
[ 'Manti', 'Somsa' ]
[ "Ko'k choy" ]Qo'lda yozilgan variant 20 qator edi, bu — 6. boshi o'zgaruvchisi, { value, done }, o'zini qaytaradigan [Symbol.iterator]() — hammasi generator ichida yashiringan.
3-mashq (qiyin): Birinchi N tasi — cheksiz bilan
Iteratsiya protokoli darsidagi 2-mashqda birinchiN(iterable, n) ni yozgan edingiz. Endi uni generator qiling: function* birinchiN(iterable, n) — iterable'dan ko'pi bilan n ta qiymat bersin (massiv yig'masin). So'ng uni chiptalar() cheksiz generatori bilan sinang: [...birinchiN(chiptalar(), 5)] qotmasdan [1, 2, 3, 4, 5] bersin.
Ishora: generator ichida for...of va hisoblagich; yetarli bo'lsa — return.
Yechim
function* chiptalar() {
let raqam = 1;
while (true) {
yield raqam++;
}
}
function* birinchiN(iterable, n) {
if (n <= 0) return;
let berildi = 0;
for (const qiymat of iterable) {
yield qiymat;
berildi++;
if (berildi === n) return;
}
}
console.log([...birinchiN(chiptalar(), 5)]); // [ 1, 2, 3, 4, 5 ]
console.log([...birinchiN("Bahor", 3)]); // [ 'B', 'a', 'h' ]Cheksiz generator ustiga cheklovchi generator qo'ydik. Spread birinchiN dan so'raydi, birinchiN esa chiptalar dan — faqat besh marta. return for...of dan chiqib ketadi, va for...of ichki chiptalar generatorini ham to'xtatadi. Natija — cheksiz ketma-ketlikdan xavfsiz bo'lak.
n dan keyin return nega yield dan keyin turibdi? Agar tekshiruv sikl boshida bo'lsa, beshinchi qiymatdan keyin birinchiN yana bir marta chiptalar dan oltinchi sonni so'rardi — keraksiz ish. Bu kichik farq keyingi darslarda katta ahamiyatga ega: Iterator helpers dagi tayyor take(n) metodi aynan shunday ishlaydi.
4-mashq: Vazifalar qadami — id yasovchi: closure va generator (mashqlar repo)
Bu mavzu vazifalar ilovasini o'zgartirmaydi — qadam mashqlar reposida: kurs/mashqlar/10/52-generator/idlar.js.
vazifalar da id'larni Closure amaliyotda darsidagi idYasovchiYarat(boshlangich) yasardi (keyin OOP tamoyillari darsida u VazifalarRoyxati ichiga ko'chdi: #yangiId = idYasovchiYarat(engKatta + 1)). Xuddi shu ishni generator bilan yozing va ikkalasini solishtiring:
idYasovchiYarat(boshlangich)— closure varianti (ko'chirib oling).function* idlar(boshlangich)— cheksiz generator.- Ikkalasidan 4 dan boshlab uchtadan id oling va chiqaring.
- Bitta gap bilan javob yozing:
vazifalaruchun qaysi biri qulayroq va nega?
Yechim
// 1. Closure: har chaqiruv — keyingi id
function idYasovchiYarat(boshlangich) {
let keyingi = boshlangich;
return () => keyingi++;
}
// 2. Generator: har next() — keyingi id (cheksiz)
function* idlar(boshlangich) {
let keyingi = boshlangich;
while (true) {
yield keyingi++;
}
}
const yangiId = idYasovchiYarat(4);
const idOqimi = idlar(4);
for (let i = 0; i < 3; i++) {
console.log(yangiId(), idOqimi.next().value);
}
// Generatorning ustunligi: protokol bilan bir qatorda
const [a, b, c] = idlar(1);
console.log(a, b, c);Konsolda:
4 4
5 5
6 6
1 2 3Ikkalasi bir xil ketma-ketlik beradi — ichida ham bir xil g'oya: keyingi o'zgaruvchisi to'xtab turgan muhitda saqlanadi. Farqi — tashqi ko'rinishida:
| Closure | Generator | |
|---|---|---|
| Keyingi id | yangiId() |
idOqimi.next().value |
for...of, destructuring |
yo'q | bor |
| Kod hajmi | 2 qator | 4 qator |
Javob: vazifalar da id'ni bitta-bitta, vazifa qo'shilganda so'raymiz — yangiId() qisqa va aniq, generatorning protokol imkoniyatlari bu yerda kerak emas. Generator esa ko'p id'ni birdaniga yoki boshqa iteratorlar bilan birga ishlatish kerak bo'lganda yutadi — masalan, sinov uchun yuzta namuna vazifa yasashda.
Commit (mashqlar repo): 10/52: id yasovchini closure va generator bilan solishtir.
9. Real ishda
- Kutubxonalardagi iteratorlar. O'z to'plam klassini yozgan har bir kutubxona
*[Symbol.iterator]()dan foydalanadi — qo'ldanext()yozish deyarli uchramaydi. Algoritmlar qismida (14-qism) daraxt bo'ylab yurishni ham generator bilan yozasiz. - Katta ma'lumot. Million qatorli faylni qatorma-qator qayta ishlash, sahifalab keladigan ma'lumot — hammasi "dangasa": xotiraga bir vaqtda bitta bo'lak. Vaqt o'tib keladigan ma'lumot uchun generatorning asinxron egizagi bor — Asinxron iteratsiya darsida.
- Holat mashinalari. Bosqichma-bosqich jarayonlar (ro'yxatdan o'tish ustasi, o'yin bosqichlari) generator bilan oddiy ketma-ket kod bo'lib yoziladi. Redux-saga (React ilovalarida holatni boshqarishga yordam beradigan kutubxona; React'ni kursda alohida o'rganamiz) butunlay generatorlar ustiga qurilgan — keyingi darsda uning g'oyasini ko'ramiz.
- Intervyu. "Generator nima va oddiy funksiyadan farqi?", "Fibonachchi sonlarini generator bilan yozing", "Cheksiz generatorni qanday xavfsiz ishlatasiz?" — o'rta darajadagi intervyularda tez-tez uchraydi.
Xulosa
function*chaqirilganda tanani bajarmaydi — generator obyektini qaytaradi.next()tanani keyingiyieldgacha bajaradi;yield x→{ value: x, done: false }va pauza. Tana tugasa —done: true.- Generator — tayyor iterator va iterable:
for...of, spread, destructuring bilan ishlaydi;returnqiymatinifor...oftashlab yuboradi. - Generator obyekti bir martalik, generator funksiya — har chaqiruvda yangi generator.
- Qiymat so'ralganda hisoblanadi (dangasa).
while (true)+yield— xavfsiz cheksiz ketma-ketlik, lekin uni spread qilmang. yieldfaqat generatorning o'z tanasida: callback ichida —SyntaxError; arrow generator yo'q.
Keyingi dars: Generatorlar chuqur — yield* bilan generatorlarni birlashtirish va next(qiymat) bilan generatorga ma'lumot yuborish.
Manbalar
- MDN: "function*", "yield", "Generator" — developer.mozilla.org
- MDN: "Iterators and generators" qo'llanmasi — developer.mozilla.org
- ECMAScript spetsifikatsiyasi: "Generator Objects" — tc39.es/ecma262
Izohlar (0)
Izoh yozish uchun kiring.
- Hozircha izoh yo'q. Birinchi bo'ling!