JavaScript generatorlari bo'yicha to'liq qo'llanma: funksionalligi, iterator protokoli, qo'llanilishi va zamonaviy JavaScript uchun ilg'or usullarni o'rganish.
JavaScript Generatorlari: Iterator Protokoli Implementatsiyasini O'zlashtirish
JavaScript generatorlari ECMAScript 6 (ES6) da taqdim etilgan kuchli xususiyat bo'lib, tilning iterativ jarayonlar va asinxron dasturlash bilan ishlash imkoniyatlarini sezilarli darajada kengaytiradi. Ular iteratorlarni aniqlashning noyob usulini taqdim etib, o'qilishi oson, qo'llab-quvvatlanadigan va samarali kod yozish imkonini beradi. Ushbu to'liq qo'llanma JavaScript generatorlari olamiga chuqur kirib boradi, ularning funksionalligini, iterator protokoli implementatsiyasini, amaliy qo'llanilish holatlarini va ilg'or usullarini o'rganadi.
Iteratorlar va Iterator Protokolini Tushunish
Generatorlarga sho'ng'ishdan oldin, iteratorlar va iterator protokoli tushunchasini tushunish juda muhim. Iterator - bu ketma-ketlikni belgilaydigan va tugagach, potentsial qaytariladigan qiymatga ega bo'lgan ob'ekt. Aniqroq aytganda, iterator - bu ikkita xususiyatga ega ob'ektni qaytaradigan next()
metodiga ega bo'lgan har qanday ob'ekt:
value
: Ketma-ketlikdagi keyingi qiymat.done
: Iteratorning tugaganligini bildiruvchi mantiqiy qiymat.true
qiymati ketma-ketlikning oxirini bildiradi.
Iterator protokoli - bu shunchaki ob'ektning o'zini iterable (iteratsiya qilinadigan) qilishining standart usulidir. Agar ob'ekt o'zining iteratsiya xatti-harakatini, masalan, for...of
konstruksiyasida qanday qiymatlar bo'yicha aylanishini belgilasa, u iterable hisoblanadi. Iterable bo'lishi uchun ob'ekt Symbol.iterator
orqali kirish mumkin bo'lgan @@iterator
metodini amalga oshirishi kerak. Ushbu metod iterator ob'ektini qaytarishi lozim.
Massivlar, satrlar, map'lar va set'lar kabi ko'plab o'rnatilgan JavaScript ma'lumotlar tuzilmalari iterator protokolini amalga oshirganligi sababli tabiatan iterable hisoblanadi. Bu bizga ularning elementlari bo'yicha for...of
tsikllari yordamida osongina aylanish imkonini beradi.
Misol: Massiv bo'yicha iteratsiya
const myArray = [1, 2, 3];
const iterator = myArray[Symbol.iterator]();
console.log(iterator.next()); // Output: { value: 1, done: false }
console.log(iterator.next()); // Output: { value: 2, done: false }
console.log(iterator.next()); // Output: { value: 3, done: false }
console.log(iterator.next()); // Output: { value: undefined, done: true }
for (const value of myArray) {
console.log(value); // Output: 1, 2, 3
}
JavaScript Generatorlari bilan Tanishtiruv
Generator - bu pauza qilinishi va qayta tiklanishi mumkin bo'lgan maxsus turdagi funksiya bo'lib, ma'lumotlar generatsiyasi oqimini boshqarish imkonini beradi. Generatorlar function*
sintaksisi va yield
kalit so'zi yordamida aniqlanadi.
function*
: Bu generator funksiyasini e'lon qiladi. Generator funksiyasini chaqirish uning tanasini darhol bajarmaydi; buning o'rniga, u generator ob'ekti deb ataladigan maxsus turdagi iteratorni qaytaradi.yield
: Ushbu kalit so'z generatorning bajarilishini pauza qiladi va chaqiruvchiga qiymat qaytaradi. Generatorning holati saqlanadi, bu esa uni keyinchalik pauza qilingan joydan qayta tiklash imkonini beradi.
Generator funksiyalari iterator protokolini amalga oshirishning ixcham va oqlangan usulini taqdim etadi. Ular holatni boshqarish va qiymatlarni yield qilish murakkabliklarini o'z zimmasiga oladigan iterator ob'ektlarini avtomatik ravishda yaratadi.
Misol: Oddiy Generator
function* numberGenerator() {
yield 1;
yield 2;
yield 3;
}
const gen = numberGenerator();
console.log(gen.next()); // Output: { value: 1, done: false }
console.log(gen.next()); // Output: { value: 2, done: false }
console.log(gen.next()); // Output: { value: 3, done: false }
console.log(gen.next()); // Output: { value: undefined, done: true }
Generatorlar Iterator Protokolini Qanday Amalga Oshiradi
Generator funksiyalari iterator protokolini avtomatik ravishda amalga oshiradi. Siz generator funksiyasini aniqlaganingizda, JavaScript avtomatik ravishda next()
metodiga ega bo'lgan generator ob'ektini yaratadi. Har safar generator ob'ektida next()
metodini chaqirganingizda, generator funksiyasi yield
kalit so'ziga duch kelguncha bajariladi. yield
kalit so'zi bilan bog'liq bo'lgan qiymat next()
tomonidan qaytarilgan ob'ektning value
xususiyati sifatida qaytariladi va done
xususiyati false
ga o'rnatiladi. Generator funksiyasi tugagach (yoki funksiya oxiriga yetganda yoki return
bayonotiga duch kelganda), done
xususiyati true
bo'ladi va value
xususiyati qaytariladigan qiymatga (yoki aniq return
bayonoti bo'lmasa undefined
ga) o'rnatiladi.
Muhimi, generator ob'ektlari ham o'zlari iterable hisoblanadi! Ularda generator ob'ektining o'zini qaytaradigan Symbol.iterator
metodi mavjud. Bu generatorlarni for...of
tsikllari va iterable ob'ektlarni kutadigan boshqa konstruksiyalar bilan ishlatishni juda oson qiladi.
JavaScript Generatorlarining Amaliy Qo'llanilish Holatlari
Generatorlar ko'p qirrali bo'lib, keng ko'lamli stsenariylarda qo'llanilishi mumkin. Mana bir nechta keng tarqalgan qo'llanilish holatlari:
1. Maxsus Iteratorlar
Generatorlar murakkab ma'lumotlar tuzilmalari yoki algoritmlar uchun maxsus iteratorlarni yaratishni soddalashtiradi. next()
metodini qo'lda amalga oshirish va holatni boshqarish o'rniga, qiymatlarni boshqariladigan tarzda ishlab chiqarish uchun yield
dan foydalanishingiz mumkin.
Misol: Binar daraxt bo'yicha iteratsiya
class Node {
constructor(value) {
this.value = value;
this.left = null;
this.right = null;
}
}
class BinaryTree {
constructor(root) {
this.root = root;
}
*[Symbol.iterator]() {
function* inOrderTraversal(node) {
if (node) {
yield* inOrderTraversal(node.left); // chap quyi daraxtdan qiymatlarni rekursiv ravishda yield qilish
yield node.value;
yield* inOrderTraversal(node.right); // o'ng quyi daraxtdan qiymatlarni rekursiv ravishda yield qilish
}
}
yield* inOrderTraversal(this.root);
}
}
// Namunaviy binar daraxt yaratish
const root = new Node(1);
root.left = new Node(2);
root.right = new Node(3);
root.left.left = new Node(4);
root.left.right = new Node(5);
const tree = new BinaryTree(root);
// Maxsus iterator yordamida daraxt bo'yicha iteratsiya
for (const value of tree) {
console.log(value); // Output: 4, 2, 5, 1, 3
}
Bu misol inOrderTraversal
generator funksiyasi binar daraxtni rekursiv ravishda aylanib chiqib, qiymatlarni tartiblangan (in-order) holda yield qilishini ko'rsatadi. yield*
sintaksisi iteratsiyani boshqa iterable'ga (bu holda, inOrderTraversal
ga rekursiv chaqiruvlar) topshirish uchun ishlatiladi, bu esa ichki joylashgan iterable'ni samarali ravishda tekislaydi.
2. Cheksiz Ketma-ketliklar
Generatorlar Fibonachchi sonlari yoki tub sonlar kabi cheksiz qiymatlar ketma-ketligini yaratish uchun ishlatilishi mumkin. Generatorlar qiymatlarni talab bo'yicha ishlab chiqarganligi sababli, qiymat haqiqatda talab qilinmaguncha xotirani sarflamaydi.
Misol: Fibonachchi Sonlarini Generatsiya Qilish
function* fibonacciGenerator() {
let a = 0;
let b = 1;
while (true) {
yield a;
[a, b] = [b, a + b];
}
}
const fib = fibonacciGenerator();
console.log(fib.next().value); // Output: 0
console.log(fib.next().value); // Output: 1
console.log(fib.next().value); // Output: 1
console.log(fib.next().value); // Output: 2
console.log(fib.next().value); // Output: 3
// ... va hokazo
fibonacciGenerator
funksiyasi Fibonachchi sonlarining cheksiz ketma-ketligini generatsiya qiladi. while (true)
tsikli generatorning qiymatlarni cheksiz ravishda ishlab chiqarishda davom etishini ta'minlaydi. Qiymatlar talab bo'yicha generatsiya qilinganligi sababli, bu generator cheksiz xotira sarflamasdan cheksiz ketma-ketlikni ifodalashi mumkin.
3. Asinxron Dasturlash
Generatorlar asinxron dasturlashda, ayniqsa promise'lar bilan birgalikda ishlatilganda, muhim rol o'ynaydi. Ular sinxron kodga o'xshab ko'rinadigan va ishlaydigan asinxron kod yozish uchun ishlatilishi mumkin, bu esa uni o'qish va tushunishni osonlashtiradi.
Misol: Generatorlar yordamida Asinxron Ma'lumotlarni Olish
function fetchData(url) {
return new Promise((resolve, reject) => {
fetch(url)
.then(response => response.json())
.then(data => resolve(data))
.catch(error => reject(error));
});
}
function* dataFetcher() {
try {
const user = yield fetchData('https://jsonplaceholder.typicode.com/users/1');
console.log('User:', user);
const posts = yield fetchData(`https://jsonplaceholder.typicode.com/posts?userId=${user.id}`);
console.log('Posts:', posts);
} catch (error) {
console.error('Error fetching data:', error);
}
}
function runGenerator(generator) {
const iterator = generator();
function iterate(result) {
if (result.done) return;
const promise = result.value;
promise
.then(value => iterate(iterator.next(value)))
.catch(error => iterator.throw(error));
}
iterate(iterator.next());
}
runGenerator(dataFetcher);
Bu misolda, dataFetcher
generator funksiyasi promise qaytaradigan fetchData
funksiyasi yordamida foydalanuvchi va post ma'lumotlarini asinxron ravishda oladi. yield
kalit so'zi promise hal bo'lguncha generatorni pauza qiladi, bu esa sizga asinxron kodni ketma-ket, sinxron uslubda yozish imkonini beradi. runGenerator
funksiyasi generatorni boshqaradigan, promise'larning hal bo'lishini va xatolarni tarqatishni boshqaradigan yordamchi funksiyadir.
Zamonaviy asinxron JavaScript uchun ko'pincha `async/await` afzal ko'rilsa-da, generatorlarning o'tmishda (va ba'zan hozir ham) asinxron boshqaruv oqimi uchun qanday ishlatilganini tushunish tilning evolyutsiyasi haqida qimmatli ma'lumot beradi.
4. Ma'lumotlar Oqimi va Qayta Ishlash
Generatorlar katta hajmdagi ma'lumotlar to'plamlari yoki ma'lumotlar oqimlarini xotirani tejaydigan tarzda qayta ishlash uchun ishlatilishi mumkin. Ma'lumotlar qismlarini bosqichma-bosqich yield qilib, butun ma'lumotlar to'plamini bir vaqtning o'zida xotiraga yuklashdan qochishingiz mumkin.
Misol: Katta CSV Faylini Qayta Ishlash
const fs = require('fs');
const readline = require('readline');
async function* processCSV(filePath) {
const fileStream = fs.createReadStream(filePath);
const rl = readline.createInterface({
input: fileStream,
crlfDelay: Infinity
});
for await (const line of rl) {
// Har bir qatorni qayta ishlash (masalan, CSV ma'lumotlarini ajratish)
const data = line.split(',');
yield data;
}
}
async function main() {
const csvGenerator = processCSV('large_data.csv');
for await (const row of csvGenerator) {
console.log('Row:', row);
// Har bir qator ustida amallarni bajarish
}
}
main();
Bu misol katta CSV faylini qatorma-qator o'qish uchun fs
va readline
modullaridan foydalanadi. processCSV
generator funksiyasi CSV faylining har bir qatorini massiv sifatida yield qiladi. async/await
sintaksisi fayl qatorlari bo'yicha asinxron iteratsiya qilish uchun ishlatiladi, bu esa faylning asosiy thread'ni bloklamasdan samarali qayta ishlanishini ta'minlaydi. Bu yerdagi asosiy g'oya - butun CSV'ni avval xotiraga yuklashga urinish o'rniga, har bir qatorni *o'qilishi bilanoq* qayta ishlashdir.
Generatorlarning Ilg'or Usullari
1. `yield*` yordamida Generator Kompozitsiyasi
yield*
kalit so'zi iteratsiyani boshqa iterable ob'ekt yoki generatorga topshirish imkonini beradi. Bu oddiyroq iteratorlardan murakkab iteratorlarni tuzish uchun foydalidir.
Misol: Bir nechta Generatorlarni Birlashtirish
function* generator1() {
yield 1;
yield 2;
}
function* generator2() {
yield 3;
yield 4;
}
function* combinedGenerator() {
yield* generator1();
yield* generator2();
yield 5;
}
const combined = combinedGenerator();
console.log(combined.next()); // Output: { value: 1, done: false }
console.log(combined.next()); // Output: { value: 2, done: false }
console.log(combined.next()); // Output: { value: 3, done: false }
console.log(combined.next()); // Output: { value: 4, done: false }
console.log(combined.next()); // Output: { value: 5, done: false }
console.log(combined.next()); // Output: { value: undefined, done: true }
combinedGenerator
funksiyasi generator1
va generator2
dan olingan qiymatlarni, shuningdek, qo'shimcha 5 qiymatini birlashtiradi. yield*
kalit so'zi ichki iteratorlarni samarali ravishda tekislaydi va yagona qiymatlar ketma-ketligini hosil qiladi.
2. `next()` yordamida Generatorlarga Qiymat Yuborish
Generator ob'ektining next()
metodi argument qabul qilishi mumkin, bu argument keyin generator funksiyasi ichidagi yield
ifodasining qiymati sifatida uzatiladi. Bu generator va chaqiruvchi o'rtasida ikki tomonlama aloqani ta'minlaydi.
Misol: Interaktiv Generator
function* interactiveGenerator() {
const input1 = yield 'What is your name?';
console.log('Received name:', input1);
const input2 = yield 'What is your favorite color?';
console.log('Received color:', input2);
return `Hello, ${input1}! Your favorite color is ${input2}.`;
}
const interactive = interactiveGenerator();
console.log(interactive.next().value); // Output: What is your name?
console.log(interactive.next('Alice').value); // Output: Received name: Alice
// Output: What is your favorite color?
console.log(interactive.next('Blue').value); // Output: Received color: Blue
// Output: Hello, Alice! Your favorite color is Blue.
console.log(interactive.next()); // Output: { value: Hello, Alice! Your favorite color is Blue., done: true }
Ushbu misolda, interactiveGenerator
funksiyasi foydalanuvchidan ismi va sevimli rangini so'raydi. next()
metodi foydalanuvchining kiritgan ma'lumotlarini generatorga qaytarib yuborish uchun ishlatiladi, so'ngra generator uni shaxsiy salomlashni tuzish uchun ishlatadi. Bu generatorlarning tashqi kiritishlarga javob beradigan interaktiv dasturlar yaratish uchun qanday ishlatilishini ko'rsatadi.
3. `throw()` yordamida Xatoliklarga Ishlov Berish
Generator ob'ektining throw()
metodi generator funksiyasi ichida istisno (exception) yuborish uchun ishlatilishi mumkin. Bu generator kontekstida xatoliklarga ishlov berish va tozalash imkonini beradi.
Misol: Generatorda Xatoliklarga Ishlov Berish
function* errorGenerator() {
try {
yield 'Starting...';
throw new Error('Something went wrong!');
yield 'This will not be executed.';
} catch (error) {
console.error('Caught error:', error.message);
yield 'Recovering...';
}
yield 'Finished.';
}
const errorGen = errorGenerator();
console.log(errorGen.next().value); // Output: Starting...
console.log(errorGen.next().value); // Output: Caught error: Something went wrong!
// Output: Recovering...
console.log(errorGen.next().value); // Output: Finished.
console.log(errorGen.next().value); // Output: undefined
Ushbu misolda, errorGenerator
funksiyasi try...catch
bloki ichida xatolik yuzaga keltiradi. catch
bloki xatolikni ushlaydi va tiklanish xabarini yield qiladi. Bu generatorlarning xatoliklarni chiroyli tarzda boshqarish va bajarilishni davom ettirish uchun qanday ishlatilishini ko'rsatadi.
4. `return()` yordamida Qiymat Qaytarish
Generator ob'ektining return()
metodi generatorni muddatidan oldin tugatish va ma'lum bir qiymatni qaytarish uchun ishlatilishi mumkin. Bu resurslarni tozalash yoki ketma-ketlikning tugaganligini bildirish uchun foydali bo'lishi mumkin.
Misol: Generatorni Erta Tugatish
function* earlyExitGenerator() {
yield 1;
yield 2;
return 'Exiting early!';
yield 3; // Bu bajarilmaydi
}
const exitGen = earlyExitGenerator();
console.log(exitGen.next().value); // Output: 1
console.log(exitGen.next().value); // Output: 2
console.log(exitGen.next().value); // Output: Exiting early!
console.log(exitGen.next().value); // Output: undefined
console.log(exitGen.next().done); // Output: true
Ushbu misolda, earlyExitGenerator
funksiyasi return
bayonotiga duch kelganda muddatidan oldin tugaydi. return()
metodi belgilangan qiymatni qaytaradi va generatorning tugaganligini bildiruvchi done
xususiyatini true
ga o'rnatadi.
JavaScript Generatorlaridan Foydalanishning Afzalliklari
- Kodning O'qilishi Osonlashadi: Generatorlar iterativ kodni yanada ketma-ket va sinxron uslubda yozish imkonini beradi, bu esa uni o'qish va tushunishni osonlashtiradi.
- Soddalashtirilgan Asinxron Dasturlash: Generatorlar asinxron kodni soddalashtirish uchun ishlatilishi mumkin, bu esa callback'lar va promise'larni boshqarishni osonlashtiradi.
- Xotira Samaradorligi: Generatorlar qiymatlarni talab bo'yicha ishlab chiqaradi, bu esa butun ma'lumotlar to'plamlarini xotirada yaratish va saqlashdan ko'ra xotirani tejaydiganroq bo'lishi mumkin.
- Maxsus Iteratorlar: Generatorlar murakkab ma'lumotlar tuzilmalari yoki algoritmlar uchun maxsus iteratorlarni yaratishni osonlashtiradi.
- Kodning Qayta Ishlatilishi: Generatorlar turli kontekstlarda tuzilishi va qayta ishlatilishi mumkin, bu esa kodning qayta ishlatilishini va qo'llab-quvvatlanishini rag'batlantiradi.
Xulosa
JavaScript generatorlari zamonaviy JavaScript dasturlash uchun kuchli vositadir. Ular iterator protokolini amalga oshirishning ixcham va oqlangan usulini taqdim etadi, asinxron dasturlashni soddalashtiradi va katta hajmdagi ma'lumotlarni samarali qayta ishlaydi. Generatorlarni va ularning ilg'or usullarini o'zlashtirish orqali siz o'qilishi oson, qo'llab-quvvatlanadigan va samaraliroq kod yozishingiz mumkin. Murakkab ma'lumotlar tuzilmalarini qurayotgan bo'lsangiz, asinxron operatsiyalarni qayta ishlayotgan bo'lsangiz yoki ma'lumotlar oqimini uzatayotgan bo'lsangiz ham, generatorlar sizga keng ko'lamli muammolarni osonlik va nafosat bilan hal qilishga yordam beradi. Generatorlarni o'zlashtirish, shubhasiz, sizning JavaScript dasturlash ko'nikmalaringizni oshiradi va loyihalaringiz uchun yangi imkoniyatlar ochadi.
JavaScriptni o'rganishda davom etar ekansiz, unutmangki, generatorlar jumboqning faqat bir qismidir. Ularni promise'lar, async/await va strelkali funksiyalar kabi boshqa zamonaviy xususiyatlar bilan birlashtirish yanada kuchli va ifodali kodga olib kelishi mumkin. Tajriba qilishda, o'rganishda va ajoyib narsalar yaratishda davom eting!