JavaScript Generatorlari va Iteratorlari

JavaScript Generatorlari va Iteratorlari to'liq qamrovli qo'llanma, ularning xususiyatlari va amaliy foydalanish holatlari.

So'nggi yangilanish: 2024-12-14

Generatorlar va Iteratorlar JavaScript'dagi kuchli xususiyatlar bo'lib, ular iteratsiya oqimini boshqarish va to'xtatilishi va davom ettirilishi mumkin bo'lgan funksiyalarni yaratish imkonini beradi. Ular ayniqsa asinxron operatsiyalarni boshqarish, cheksiz ketma-ketliklarni yaratish va katta ma'lumotlar to'plamlari bilan samarali ishlash uchun foydalidir. Ushbu qo'llanma bu tushunchalarni chuqur tushuntirish, amaliy misollar va eng yaxshi amaliyotlarni taqdim etishga qaratilgan.

Iteratorlar nima?

Iteratorlar next() metodini belgilaydigan obyektlar bo'lib, u value va done xususiyatlariga ega obyektni qaytaradi. Ular to'plamning elementlariga birma-bir kirish imkonini beradi.

Generatorlar nima?

Generatorlar to'xtatilishi va davom ettirilishi mumkin bo'lgan maxsus funksiyalardir, ular bajarilishi uzluksiz bo'lmagan yagona funksiya yozish orqali iterativ algoritmni belgilash imkonini beradi.

Iterator Protokoli

Iterator protokoli obyektdan qiymatlar ketma-ketligini qanday ishlab chiqarishni belgilaydi. Obyekt quyidagi semantikaga ega next() metodini amalga oshirsa, u iterator hisoblanadi:

const iterator = {
  next: function() {
    return {
      value: any,
      done: boolean
    };
  }
};

Iterable Protokoli

Iterable protokoli JavaScript obyektlariga o'zlarining iteratsiya xatti-harakatlarini belgilash yoki moslashtirish imkonini beradi. Agar obyekt @@iterator metodini amalga oshirsa, ya'ni Symbol.iterator kalitiga ega xususiyati bo'lsa, u iterable hisoblanadi.

const iterable = {
  [Symbol.iterator]: function() {
    return iterator;
  }
};

Maxsus Iteratorlar Yaratish

Mana maxsus iterator yaratish misoli:

function oraliqIterator(boshlash, tugatish, qadam) {
  let joriy = boshlash;
  return {
    next: function() {
      if (joriy <= tugatish) {
        const natija = { value: joriy, done: false };
        joriy += qadam;
        return natija;
      }
      return { value: undefined, done: true };
    }
  };
}

const iterator = oraliqIterator(0, 10, 2);
console.log(iterator.next()); // { value: 0, done: false }
console.log(iterator.next()); // { value: 2, done: false }
console.log(iterator.next()); // { value: 4, done: false }
console.log(iterator.next()); // { value: 6, done: false }
console.log(iterator.next()); // { value: 8, done: false }
console.log(iterator.next()); // { value: 10, done: false }
console.log(iterator.next()); // { value: undefined, done: true }

Generator Funksiyalari

Generator funksiyalari maxsus iteratorlarga kuchli muqobil variant taqdim etadi. Ular yulduzcha (*) yordamida aniqlanadi va bajarilishni to'xtatish va davom ettirish uchun yield kalit so'zidan foydalanadi.

function* oraliqGenerator(boshlash, tugatish, qadam) {
  for (let i = boshlash; i <= tugatish; i += qadam) {
    yield i;
  }
}

const generator = oraliqGenerator(0, 10, 2);
console.log(generator.next()); // { value: 0, done: false }
console.log(generator.next()); // { value: 2, done: false }
console.log(generator.next()); // { value: 4, done: false }
console.log(generator.next()); // { value: 6, done: false }
console.log(generator.next()); // { value: 8, done: false }
console.log(generator.next()); // { value: 10, done: false }
console.log(generator.next()); // { value: undefined, done: true }

Yield Kalit So'zi

yield kalit so'zi generator funksiyalarida funksiya bajarilishini to'xtatish va davom ettirish mumkin bo'lgan nuqtalarni belgilash uchun ishlatiladi.

function* teskariSanash(boshlash) {
  while (boshlash > 0) {
    yield boshlash;
    boshlash--;
  }
}

const sanagich = teskariSanash(3);
console.log(sanagich.next().value); // 3
console.log(sanagich.next().value); // 2
console.log(sanagich.next().value); // 1
console.log(sanagich.next().done);  // true

Generator Metodlari

Generatorlarning bir nechta metodlari mavjud:

  1. next(): Ketma-ketlikdagi keyingi qiymatni qaytaradi.
  2. return(): Generatorni tugatadi.
  3. throw(): Xatoni chiqaradi va generatorni tugatadi, agar xato ushlangan bo'lmasa.
function* misolGenerator() {
  yield 1;
  yield 2;
  yield 3;
}

const gen = misolGenerator();
console.log(gen.next());     // { value: 1, done: false }
console.log(gen.return(10)); // { value: 10, done: true }
console.log(gen.next());     // { value: undefined, done: true }

Asinxron Generatorlar

Asinxron generatorlar generatorlarni Promise'lar bilan birlashtiradi, bu asinxron iteratsiyaga imkon beradi.

async function* asinxronGenerator() {
  yield await Promise.resolve(1);
  yield await Promise.resolve(2);
  yield await Promise.resolve(3);
}

(async () => {
  for await (const qiymat of asinxronGenerator()) {
    console.log(qiymat);
  }
})();
// Natija:
// 1
// 2
// 3

Amaliy Foydalanish Holatlari

  1. Dangasa baholashni (lazy evaluation) amalga oshirish:
function* dangasaOraliq(boshlash, tugatish) {
  for (let i = boshlash; i <= tugatish; i++) {
    yield i;
  }
}

const oraliq = dangasaOraliq(1, 1000000);
console.log(oraliq.next().value); // 1
console.log(oraliq.next().value); // 2
// Oraliqning qolgan qismi so'ralmagunga qadar hisoblanmaydi
  1. Asinxron operatsiyalarni boshqarish:
async function* sahifalarniOlish(url_lar) {
  for (const url of url_lar) {
    const javob = await fetch(url);
    yield await javob.text();
  }
}

(async () => {
  const url_lar = ['https://api.example.com/sahifa1', 'https://api.example.com/sahifa2'];
  for await (const sahifa of sahifalarniOlish(url_lar)) {
    console.log(sahifa);
  }
})();
  1. Maxsus iterable'larni amalga oshirish:
class FibonacchiKetmaKetligi {
  constructor(chegara) {
    this.chegara = chegara;
  }

  *[Symbol.iterator]() {
    let a = 0, b = 1, sanoq = 0;
    while (sanoq < this.chegara) {
      yield a;
      [a, b] = [b, a + b];
      sanoq++;
    }
  }
}

const fib = new FibonacchiKetmaKetligi(5);
for (const son of fib) {
  console.log(son);
}
// Natija:
// 0
// 1
// 1
// 2
// 3

Eng Yaxshi Amaliyotlar

  1. Mantiq murakkab bo'lganda iterable'lar yaratish uchun generatorlardan foydalaning.
  2. Generatorlar va iterable'lar bilan ishlashda for...of tsikllarini afzal ko'ring.
  3. Asinxron ma'lumotlar oqimlarini boshqarish uchun asinxron generatorlardan foydalaning.
  4. Iterable bo'lishi kerak bo'lgan maxsus ma'lumotlar tuzilmalari uchun iterable protokolini amalga oshiring.

Umumiy Xatolar

  1. Generatorlar bir martalik ishlatilishini unutish:
function* birMartalikGenerator() {
  yield 1;
  yield 2;
}

const gen = birMartalikGenerator();
console.log([...gen]); // [1, 2]
console.log([...gen]); // [] (generator allaqachon tugagan)
  1. Generatorlarning dangasa baholash tabiatini noto'g'ri tushunish:
function* dangasaGenerator() {
  console.log("Birinchi yield");
  yield 1;
  console.log("Ikkinchi yield");
  yield 2;
}

const gen = dangasaGenerator();
console.log(gen.next()); // Chop etadi: "Birinchi yield", keyin { value: 1, done: false }
// "Ikkinchi yield" next() ga keyingi murojaat qilinmaguncha chop etilmaydi

Generatorlar va Async/Await

Async/await asinxron operatsiyalarni boshqarish uchun oddiyroq usul taqdim etsa-da, generatorlar asinxron oqim ustidan yanada nozikroq nazorat taklif qiladi:

// Async/await dan foydalanish
async function ma'lumotOlish(url) {
  const javob = await fetch(url);
  return await javob.json();
}

// Generatorlardan foydalanish
function* ma'lumotOlishGen(url) {
  const javob = yield fetch(url);
  const ma'lumot = yield javob.json();
  return ma'lumot;
}

function generatorYurgazish(genFn) {
  const gen = genFn();
  function bajarish(natija) {
    if (natija.done) return Promise.resolve(natija.value);
    return Promise.resolve(natija.value).then(
      res => bajarish(gen.next(res)),
      err => bajarish(gen.throw(err))
    );
  }
  return bajarish(gen.next());
}

generatorYurgazish(ma'lumotOlishGen.bind(null, 'https://api.example.com/ma'lumot'))
  .then(ma'lumot => console.log(ma'lumot))
  .catch(xato => console.error(xato));

Ishlash Samaradorligi Mulohazalari

  1. Generatorlar oddiy funksiyalarga nisbatan kichik ishlash qo'shimcha xarajatiga ega.
  2. Oddiy iteratsiyalar uchun an'anaviy tsikllar generatorlardan tezroq bo'lishi mumkin.
  3. Generatorlar katta yoki cheksiz ketma-ketliklar uchun xotira samaradorligida ustunlikka ega.

Brauzer va Node.js Qo'llab-quvvatlashi

Generatorlar zamonaviy brauzerlar va Node.js versiyalarida yaxshi qo'llab-quvvatlanadi. Biroq, eski muhitlar uchun Babel kabi transpilerdan foydalanishingiz kerak bo'lishi mumkin.

Tez-tez So'raladigan Savollar

  1. Savol: Generator funksiyasi ichida awaitdan foydalana olamanmi? Javob: Yo'q, oddiy generator funksiyasida to'g'ridan-to'g'ri awaitdan foydalana olmaysiz. Biroq, siz yieldni Promise'lar bilan ishlatishingiz yoki asinxron generator funksiyalaridan foydalanishingiz mumkin.
  2. Savol: Generatorni massivga qanday o'girishim mumkin? Javob: Siz yoyish operatoridan foydalanishingiz mumkin: const massiv = [...meningGeneratorim()]
  3. Savol: Generatorlar iteratorlarning barcha foydalanish holatlarini almashtira oladimi? Javob: Generatorlar ko'p maxsus iteratorlar foydalanish holatlarini almashtirishi mumkin bo'lsa-da, maxsus iterator amalga oshirish ko'proq nazorat yoki yaxshiroq ishlash samaradorligini ta'minlaydigan holatlar bo'lishi mumkin.

Qo'shimcha Manbalar

  1. MDN Web Docs: Iteratorlar va Generatorlar
  2. Exploring JS: Generatorlar
  3. JavaScript.info: Generatorlar
  4. 2ality: ES6 Generatorlari chuqur ko'rib chiqilishi
  5. You Don't Know JS: Async & Performance