IlmHamroh
JavaScript Full-stack/10-qism. JavaScript chuqur52/65-dars20 daqiqa
Mundarija (34)

JavaScript generator funksiyalar: function* va yield bilan to'xtab-davom etish

Qisqacha: Generator funksiya (function*) chaqirilganda tanasini darhol bajarmaydi — generator obyektini qaytaradi. Har next() tanani keyingi yield gacha bajaradi, yield dagi qiymatni { value, done: false } qilib beradi va funksiyani shu joyda to'xtatib qo'yadi. Keyingi next() 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* va yield bilan generator yozasiz va next() qanday "to'xtatib-davom ettirishini" kuzatasiz.
  • Generator iteratsiya protokoliga to'liq amal qilishini — for...of, spread, destructuring bilan ishlashini ko'rasiz.
  • O'tgan darsdagi oraliq va klass iteratorini generator bilan qayta yozasiz.
  • Dangasa (lazy) va cheksiz ketma-ketliklarni yasaysiz va ularni xavfsiz ishlatasiz.
  • yield qayerda ishlamasligini va Unexpected strict mode reserved word xatosini 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:

js
function oraliqniChiqar(boshi, oxiri) {
  for (let i = boshi; i < oxiri; i++) {
    console.log(i);
  }
}
oraliqniChiqar(10, 13);

Konsolda:

text
10
11
12

Lekin 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

js
function* taomlar() {
  console.log("Generator boshlandi");
  yield "osh";
  yield "manti";
}

const g = taomlar();
console.log("Funksiya chaqirildi");
console.log(g);

Konsolda:

text
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) — function dan 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:

js
console.log(g.next());
console.log(g.next());
console.log(g.next());

Konsolda:

text
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. 1-next() — tana boshidan ishga tushdi: "Generator boshlandi" chiqdi, birinchi yield "osh" ga yetdi. "osh" tashqariga berildi, funksiya to'xtadi.
  2. 2-next() — to'xtagan joydan, yield "osh" dan keyingi qatordan davom etdi. Ikkinchi yield ga yetdi — "manti" berildi, yana to'xtadi.
  3. 3-next() — davom etdi, lekin yield qolmadi: 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 true

Ikki 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:

js
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 yield bor. for...of uni oxirigacha aylanganda next() necha marta chaqiriladi? Generator tanasining oxirgi yield dan 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):

js
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:

text
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

js
const bitta = bahorTaomlari();
console.log([...bitta].length); // 3
console.log([...bitta].length); // 0
console.log([...bahorTaomlari()].length); // 3

Generator 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:

js
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]:

js
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: Menyu dagi 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):

js
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:

text
Oshxona: osh tayyorlanmoqda
Ofitsiant: osh stolga
Oshxona: manti tayyorlanmoqda
Ofitsiant: manti stolga

Ikki narsaga qarang:

  1. 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.
  2. Somsa umuman tayyorlanmadi. Ofitsiant break qildi — 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() dagi for...of dan break ni 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:

js
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); // 3

while (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:

js
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:

text
1 2 3
Chipta: 1
Chipta: 2

Destructuring 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()) yoki break siz for...of — dastur abadiy ishlaydi va xotira tugaguncha massiv yig'adi. Terminalda Ctrl+C bilan 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:

js
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 Otabek

Ichki 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 keyin e nimaga 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

js
function taomlar() {
  yield "osh";
}

Konsolda:

text
SyntaxError: Unexpected strict mode reserved word

Tarjimasi: "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

js
function* taomlar(royxat) {
  royxat.forEach((taom) => {
    yield taom;
  });
}

Konsolda:

text
SyntaxError: Unexpected strict mode reserved word

Xuddi 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:

js
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
js
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); // Dushanba

Har 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.

js
const taomlar = ["Osh", "Lag'mon", "Manti", "Somsa", "Ko'k choy"];
console.log([...sahifalar(taomlar, 2)].length); // 3
Yechim
js
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:

text
[ '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
js
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:

  1. idYasovchiYarat(boshlangich) — closure varianti (ko'chirib oling).
  2. function* idlar(boshlangich) — cheksiz generator.
  3. Ikkalasidan 4 dan boshlab uchtadan id oling va chiqaring.
  4. Bitta gap bilan javob yozing: vazifalar uchun qaysi biri qulayroq va nega?
Yechim
js
// 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:

text
4 4
5 5
6 6
1 2 3

Ikkalasi 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'lda next() 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 keyingi yield gacha bajaradi; yield x → { value: x, done: false } va pauza. Tana tugasa — done: true.
  • Generator — tayyor iterator va iterable: for...of, spread, destructuring bilan ishlaydi; return qiymatini for...of tashlab 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.
  • yield faqat 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
Ulashish:Telegram'da

Izohlar (0)

Izoh yozish uchun kiring.

  • Hozircha izoh yo'q. Birinchi bo'ling!
JavaScript generator funksiyalar: function va yield bilan to'xtab-davom etish — IlmHamroh