Iterator va Generator
Muammodan boshlaylik
Ikkita vaziyat:
Birinchi: Siz 1 dan 1 000 000 gacha sonlar ketma-ketligini yaratmoqchisiz. Massiv bilan: [1, 2, 3, ..., 1000000] — bu 1 million elementni xotirada saqlaydi. Faqat bittasini ishlatsangiz ham — hammasi yuklanadi.
Ikkinchi: Fibonacci sonlar ketma-ketligi — cheksiz. [1, 1, 2, 3, 5, 8, 13, ...] — oxiri yo'q. Buni massivga sig'dirib bo'lmaydi.
Massiv bu hollarda to'g'ri tanlov emas. Kerak bo'lganida, bitta-bitta bering — oldindan hammani tayyorlamang.
Bu muammoni Iterator va Generator hal qiladi.
Iterator nima?
Iterator — "keyingi elementni ber" deb so'rash imkoniyati. Navbatma-navbat, bittadan.
Hayotiy o'xshatma
Poliklinika navbati. Palatada 50 nafar bemor bor. Ularni birdaniga kabinet ichiga kiritish mumkin emas. Hamshira "keyingisi!" deydi — biri kiradi, chiqadi, keyin yana "keyingisi!" Navbat tugaguncha davom etadi.
Iterator — o'sha "keyingisi!" tizimi. Siz next() deysiz — u bitta element beradi. Yana next() — yana bitta. Tugaganda — "tamom" belgisi beradi.
Iterator protokoli
Iterator — next() metodi bo'lgan ob'ekt. next() chaqirilganda { value, done } ob'ektini qaytaradi:
value— hozirgi elementdone—falsebo'lsa davom etmoqda,truebo'lsa tugadi
// Oddiy iterator yaratish — qo'lda
function sonlarIteratori(boshlanish, tugash) {
let hozirgi = boshlanish;
return {
next() {
if (hozirgi <= tugash) {
return { value: hozirgi++, done: false };
} else {
return { value: undefined, done: true };
}
}
};
}
const iterator = sonlarIteratori(1, 4);
console.log(iterator.next()); // { value: 1, done: false }
console.log(iterator.next()); // { value: 2, done: false }
console.log(iterator.next()); // { value: 3, done: false }
console.log(iterator.next()); // { value: 4, done: false }
console.log(iterator.next()); // { value: undefined, done: true }
Har next() — bitta qadam. 1 million sonlar uchun ham xotiraga barchasi yuklanmaydi — faqat hozirgi son.
Iterable — for...of bilan ishlash
Siz for...of ni massivda ishlatgansiz. Lekin for...of aslida qanday ishlaydi?
for...of — ob'ektdan [Symbol.iterator]() metodini chaqiradi, iterator oladi, keyin done: true bo'lguncha next() chaqiraveradi.
const massiv = [10, 20, 30];
// for...of ichida nima bo'layotgani:
const iterator = massiv[Symbol.iterator]();
console.log(iterator.next()); // { value: 10, done: false }
console.log(iterator.next()); // { value: 20, done: false }
console.log(iterator.next()); // { value: 30, done: false }
console.log(iterator.next()); // { value: undefined, done: true }
Demak massiv — iterable, chunki unda [Symbol.iterator] metodi bor. String, Map, Set ham iterable.
O'z iterable ob'ektini yaratish
const diapazon = {
bosh: 1,
oxir: 5,
[Symbol.iterator]() { // ← for...of shu metodga murojaat qiladi
let hozirgi = this.bosh;
const oxir = this.oxir;
return {
next() {
if (hozirgi <= oxir) {
return { value: hozirgi++, done: false };
}
return { value: undefined, done: true };
}
};
}
};
for (const son of diapazon) {
console.log(son); // 1, 2, 3, 4, 5
}
// Spread ham ishlaydi
console.log([...diapazon]); // [1, 2, 3, 4, 5]
Generator — iterator yozishni osonlashtiradi
Yuqoridagi iterator kodi ko'p — next(), { value, done }, holat saqlash. Generator — bularni avtomatik qiladi.
Generator — function* bilan e'lon qilinadigan maxsus funksiya. yield kalit so'zi — "mana bu qiymat, to'xta — keyingi next() da davom et".
Hayotiy o'xshatma
Kitob o'qish. Har kuni bir bob o'qiysiz — kitobni yopasiz, joy belgilanadi. Ertasi kuni davom etasiz o'sha joydan. Qaytadan boshlamaysiz.
Generator funksiya — o'sha kitob. yield — joy belgisi. Funksiya qolipida "muzlaydi", next() da davom etadi. Holat saqlanib qoladi.
function* sonlarGeneratori(bosh, oxir) {
for (let i = bosh; i <= oxir; i++) {
yield i; // ← "mana i, to'xta"
}
// ← funksiya tugadi — done: true
}
const gen = sonlarGeneratori(1, 4);
console.log(gen.next()); // { value: 1, done: false }
console.log(gen.next()); // { value: 2, done: false }
console.log(gen.next()); // { value: 3, done: false }
console.log(gen.next()); // { value: 4, done: false }
console.log(gen.next()); // { value: undefined, done: true }
for...of bilan:
for (const son of sonlarGeneratori(1, 5)) {
console.log(son); // 1, 2, 3, 4, 5
}
Generator avtomatik iterable — [Symbol.iterator] o'zi bor.
Cheksiz ketma-ketlik
Generator cheksiz bo'lishi mumkin — yield doim bo'lsa, funksiya hech qachon tugamaydi. Lekin siz qachon to'xtashni belgilaysiz.
function* fibonacci() {
let [oldingi, hozirgi] = [0, 1];
while (true) { // ← cheksiz loop — lekin generator muzlaydi
yield hozirgi; // ← hozirgi sonni ber, to'xta
[oldingi, hozirgi] = [hozirgi, oldingi + hozirgi];
}
}
const fib = fibonacci();
console.log(fib.next().value); // 1
console.log(fib.next().value); // 1
console.log(fib.next().value); // 2
console.log(fib.next().value); // 3
console.log(fib.next().value); // 5
console.log(fib.next().value); // 8
// Xohlagancha davom etish mumkin — xotira sarflanmaydi
Fibonacci sonlarining barchasi xotirada yo'q — faqat ikkita o'zgaruvchi: oldingi va hozirgi. Har next() da faqat shu ikki qiymat yangilanadi.
Generator ga qiymat berish — yield ikki tomonlama
yield — faqat qiymat chiqarmaydi. next(qiymat) orqali generatarga ham qiymat yuborish mumkin:
function* muloqot() {
const ism = yield "Ismingiz nima?"; // ← savol berdi, javob kutdi
const yosh = yield `Salom, ${ism}! Yoshingiz?`; // ← ism keldi, yana savol
yield `${ism}, ${yosh} yosh — ajoyib!`;
}
const gen = muloqot();
console.log(gen.next().value); // "Ismingiz nima?"
console.log(gen.next("Jasur").value); // "Salom, Jasur! Yoshingiz?"
console.log(gen.next(25).value); // "Jasur, 25 yosh — ajoyib!"
Birinchi next() ga argument berish shart emas — birinchi yield gacha yetib boradi.
Amaliy misol — sahifalangan ma'lumot yuklash
Generator bilan sahifama-sahifa ma'lumot yuklash qulay:
async function* sahifalab(url) {
let sahifa = 1;
while (true) {
const javob = await fetch(`${url}?sahifa=${sahifa}`);
const data = await javob.json();
if (data.items.length === 0) break; // ← ma'lumot tugadi
yield data.items; // ← shu sahifaning elementlarini ber
sahifa++;
}
}
// Ishlatish
for await (const elementlar of sahifalab("/api/mahsulotlar")) {
elementlar.forEach(el => ko_rsat(el));
// Biror shartda to'xtatish mumkin
if (sahifaYetarli()) break;
}
Bu yerda for await...of — async generator uchun. Har iteratsiyada yangi sahifa yuklanadi — oldindan hammasi emas.
Tez-tez uchraydigan xatolar
Generator funksiya chaqirilsa — darhol ishlamaydi:
function* hisob() {
console.log("Boshladi");
yield 1;
console.log("Davom etdi");
yield 2;
}
const gen = hisob(); // ← "Boshladi" chiqmadi! Generator hozirgina yaratildi
gen.next(); // "Boshladi" — endi birinchi yield gacha ishga tushdi
gen.next(); // "Davom etdi" — ikkinchi yield gacha
Generator funksiyasi chaqirilganda — ichidagi kod ishlamaydi. Faqat iterator ob'ekt yaratiladi. Kod next() da boshlaydi.
yield dan keyin return — done: true:
function* misol() {
yield 1;
yield 2;
return "tugadi"; // ← { value: "tugadi", done: true }
yield 3; // ← hech qachon yetib kelmaydi
}
const gen = misol();
console.log(gen.next()); // { value: 1, done: false }
console.log(gen.next()); // { value: 2, done: false }
console.log(gen.next()); // { value: "tugadi", done: true }
console.log(gen.next()); // { value: undefined, done: true }
for...of done: true bo'lgan value ni e'tiborsiz qoldiradi — faqat done: false lar ko'rsatiladi.
Bir xil generatorni ikki joyda ishlatish:
Generator ob'ekti — bir yo'nalishli. Tugagan generator qaytadan boshlanmaydi.
function* gen() { yield 1; yield 2; }
const iterator = gen();
for (const v of iterator) { console.log(v); } // 1, 2
for (const v of iterator) { console.log(v); } // ← hech narsa — tugab bo'ldi
// ✅ — har safar yangi generator
for (const v of gen()) { console.log(v); } // 1, 2
for (const v of gen()) { console.log(v); } // 1, 2
Xulosa
Iterator — next() metodi orqali elementlarni bittadan beruvchi ob'ekt. { value, done } protokoli. Xotira tejash — hammasi oldindan yuklanmaydi.
Iterable — [Symbol.iterator]() metodi bo'lgan ob'ekt. for...of, spread, destructuring shu metoddan foydalanadi.
Generator — function* va yield bilan yoziladigan iterator. Holat avtomatik saqlanadi, yield da "muzlaydi", next() da davom etadi.
Qachon kerak:
- Katta yoki cheksiz ketma-ketliklar
- Xotirani tejash — hammasi oldindan yuklanmasin
for...ofbilan ishlaydigan o'z ob'ektlari- Sahifalangan ma'lumot yuklash
Keyingi dars
59-dars: Symbol — nima va qachon kerak