JavaScript generator protokoli kengaytmalarining murakkab, yuqori samarali va kompozitsiyaga qodir iteratsiya shablonlarini yaratishda qanday yordam berishini o'rganing.
JavaScript Generator Protocol Kengaytmasi: Takomillashtirilgan Iterator Interfeysini O'zlashtirish
JavaScriptning dinamik dunyosida samarali ma'lumotlarni qayta ishlash va boshqaruv oqimini boshqarish muhim ahamiyatga ega. Zamonaviy ilovalar doimiy ravishda ma'lumotlar oqimi, asinxron operatsiyalar va murakkab ketma-ketliklar bilan shug'ullanib, mustahkam va nafis yechimlarni talab qiladi. Ushbu keng qamrovli qo'llanma JavaScript Generatorlarining qiziqarli olamiga, xususan, oddiy iteratorni kuchli, ko'p qirrali vositaga aylantiruvchi protokol kengaytmalariga e'tibor qaratadi. Biz ushbu yaxshilanishlar ishlab chiquvchilarga ma'lumotlar quvurlaridan tortib asinxron ish oqimlarigacha bo'lgan son-sanoqsiz murakkab stsenariylar uchun yuqori samarali, kompozitsiyaga qodir va o'qilishi mumkin bo'lgan kodni yaratishga qanday imkon berishini ko'rib chiqamiz.
Generatorlarning ilg'or imkoniyatlariga ushbu sayohatni boshlashdan oldin, JavaScriptda iteratorlar va iterables asosiy tushunchalarini qisqacha qayta ko'rib chiqaylik. Ushbu asosiy qurilish bloklarini tushunish generatorlarning imkoniyatlarini qadrlash uchun juda muhimdir.
Asoslar: JavaScriptda Iterables va Iteratorlar
JavaScriptda iteratsiya tushunchasi ikki asosiy protokolga asoslanadi:
- Iterable Protokoli: Ob'ektning
for...ofsiklidan foydalanib qanday iteratsiya qilinishini belgilaydi. Agar ob'ektda iteratorni qaytaradigan[Symbol.iterator]nomli metod mavjud bo'lsa, u iterable hisoblanadi. - Iterator Protokoli: Ob'ektning qiymatlar ketma-ketligini qanday ishlab chiqarishini belgilaydi. Agar ob'ektda ikkita xossaga ega ob'ektni qaytaradigan
next()metodi mavjud bo'lsa, u iterator hisoblanadi:value(ketma-ketlikdagi keyingi element) vadone(ketma-ketlik tugaganligini bildiruvchi mantiqiy qiymat).
Iterable Protokolini Tushunish (Symbol.iterator)
[Symbol.iterator] kaliti orqali kirish mumkin bo'lgan metodga ega har qanday ob'ekt iterable deb hisoblanadi. Bu metod chaqirilganda, iteratorni qaytarishi kerak. Massivlar, Satrlar, Xaritalar va To'plamlar kabi o'rnatilgan turlar tabiiy ravishda iterable hisoblanadi.
Oddiy massivni ko'rib chiqing:
const myArray = [1, 2, 3];
const iterator = myArray[Symbol.iterator]();
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: undefined, done: true }
for...of sikli qiymatlarni iteratsiya qilish uchun ushbu protokolni ichki ravishda ishlatadi. U iteratorni olish uchun bir marta avtomatik ravishda [Symbol.iterator]() ni chaqiradi, so'ngra done true bo'lguncha next() ni takroran chaqiradi.
Iterator Protokolini Tushunish (next(), value, done)
Iterator Protokoliga rioya qiluvchi ob'ekt next() metodini ta'minlaydi. next() ga har bir chaqiriq ikkita asosiy xossaga ega ob'ektni qaytaradi:
value: Ketma-ketlikdagi haqiqiy ma'lumot elementi. Bu har qanday JavaScript qiymati bo'lishi mumkin.done: Mantiqiy bayroq.falseko'proq qiymatlar ishlab chiqarish kerakligini bildiradi;trueiteratsiya tugaganligini bildiradi vavalueko'pinchaundefinedbo'ladi (garchi u texnik jihatdan har qanday yakuniy natija bo'lishi mumkin).
Iteratorni qo'lda amalga oshirish murakkab bo'lishi mumkin:
function createRangeIterator(start, end) {
let current = start;
return {
next() {
if (current <= end) {
return { value: current++, done: false };
} else {
return { value: undefined, done: true };
}
}
};
}
const range = createRangeIterator(1, 3);
console.log(range.next()); // { value: 1, done: false }
console.log(range.next()); // { value: 2, done: false }
console.log(range.next()); // { value: 3, done: false }
console.log(range.next()); // { value: undefined, done: true }
Generatorlar: Iterator Yaratishni Sodda Lashtirish
Bu yerda generatorlar o'z ahamiyatini ko'rsatadi. ECMAScript 2015 (ES6) da joriy qilingan generator funksiyalari (function* bilan e'lon qilingan) iteratorlarni yozishning ancha ergonomik usulini ta'minlaydi. Generator funksiyasi chaqirilganda, u o'z tanasini darhol bajarmaydi; aksincha, u Generator Ob'ektini qaytaradi. Bu ob'ektning o'zi ham Iterable, ham Iterator Protokollariga mos keladi.
Sehr yield kalit so'zi bilan sodir bo'ladi. yield uchraganda, generator bajarishni to'xtatadi, berilgan qiymatni qaytaradi va o'z holatini saqlaydi. Generator ob'ektida next() yana chaqirilganda, bajarish to'xtagan joydan davom etadi, navbatdagi yield ga yoki funksiya tanasi tugashigacha davom etadi.
Oddiy Generator Misoli
createRangeIterator ni generator yordamida qayta yozaylik:
function* rangeGenerator(start, end) {
for (let i = start; i <= end; i++) {
yield i;
}
}
const myRange = rangeGenerator(1, 3);
console.log(myRange.next()); // { value: 1, done: false }
console.log(myRange.next()); // { value: 2, done: false }
console.log(myRange.next()); // { value: 3, done: false }
console.log(myRange.next()); // { value: undefined, done: true }
// Generatorlar ham iterable, shuning uchun ularni to'g'ridan-to'g'ri for...of bilan ishlatishingiz mumkin:
console.log("Using for...of:");
for (const num of rangeGenerator(4, 6)) {
console.log(num); // 4, 5, 6
}
Qo'lda iteratorni amalga oshirishga nisbatan generator versiyasi qanchalik toza va tushunarli ekanligini ko'ring. Bu fundamental imkoniyatning o'zi generatorlarni juda foydali qiladi. Ammo ularning kuchi, ayniqsa, biz ularning protokol kengaytmalariga kirganimizda, bundan ham ko'proqdir.
Takomillashtirilgan Iterator Interfeysi: Generator Protokoli Kengaytmalar
Generator protokolining "kengaytmasi" shunchaki qiymatlarni berishdan tashqari imkoniyatlarni nazarda tutadi. Ushbu yaxshilanishlar generatorlar va ularni chaqiruvchilar orasida katta nazorat, kompozitsiya va aloqa uchun mexanizmlarni ta'minlaydi. Xususan, biz delegatsiya uchun yield* ni, qiymatlarni generatorlarga qaytarishni, shuningdek, generatorlarni xatoliklar bilan yoki muloyimlik bilan tugatishni ko'rib chiqamiz.
1. yield*: Boshqa Iterablesga Delegatsiya
yield* (yield-star) ifodasi generatorga boshqa iterable ob'ektiga delegatsiya qilish imkonini beruvchi kuchli xususiyatdir. Bu shuni anglatadiki, u boshqa iterabledan barcha qiymatlarni samarali ravishda "berishi" mumkin, delegatsiya qilingan iterable tugaguncha o'z bajarilishini to'xtatadi. Bu oddiyroq iteratsiya shablonlaridan murakkab iteratsiya shablonlarini yaratish, modullikni va qayta ishlatish imkoniyatini oshirish uchun juda foydalidir.
yield* qanday ishlaydi
Generator yield* iterable ga duch kelganda, u quyidagilarni bajaradi:
- U
iterableob'ektidan iteratorni oladi. - Keyin u ushbu ichki iterator tomonidan ishlab chiqarilgan har bir qiymatni berishni boshlaydi.
- Delegatsiya qiluvchi generatorga uning
next()metodi orqali qaytarilgan har qanday qiymat delegatsiya qilingan iteratorningnext()metodiga uzatiladi. - Agar delegatsiya qilingan iterator xato bersa, bu xato delegatsiya qiluvchi generatorga qaytariladi.
- Eng muhimi, delegatsiya qilingan iterator tugagach (uning
next()metodi{ done: true, value: X }ni qaytarganda),Xqiymati delegatsiya qiluvchi generatordagiyield*ifodasining qaytarilgan qiymatiga aylanadi. Bu ichki iteratorlarga yakuniy natijani qaytarishga imkon beradi.
Amaliy Misol: Iteratsiya Ketma-ketliklarini Birlashtirish
function* naturalNumbers() {
yield 1;
yield 2;
yield 3;
}
function* evenNumbers() {
yield 2;
yield 4;
yield 6;
}
function* combinedNumbers() {
console.log("Starting natural numbers...");
yield* naturalNumbers(); // naturalNumbers generatoriga delegatsiya qiladi
console.log("Finished natural numbers, starting even numbers...");
yield* evenNumbers(); // evenNumbers generatoriga delegatsiya qiladi
console.log("All numbers processed.");
}
const combined = combinedNumbers();
for (const num of combined) {
console.log(num);
}
// Natija:
// Starting natural numbers...
// 1
// 2
// 3
// Finished natural numbers, starting even numbers...
// 2
// 4
// 6
// All numbers processed.
Ko'rib turganingizdek, yield* naturalNumbers va evenNumbers chiqishini bitta, uzluksiz ketma-ketlikka muammosiz birlashtiradi, delegatsiya qiluvchi generator esa umumiy oqimni boshqaradi va delegatsiya qilingan ketma-ketliklar atrofida qo'shimcha mantiq yoki xabarlarni kiritishi mumkin.
Qaytariladigan Qiymatlar bilan yield*
yield* ning eng kuchli jihatlaridan biri uning delegatsiya qilingan iteratorning yakuniy qaytariladigan qiymatini olish qobiliyatidir. Generator return bayonotidan foydalanib qiymatni aniq qaytarishi mumkin. Bu qiymat oxirgi next() chaqiruvining value xususiyati tomonidan, shuningdek, agar u ushbu generatorga delegatsiya qilsa, yield* ifodasi tomonidan ham olinadi.
function* processData(data) {
let sum = 0;
for (const item of data) {
sum += item;
yield item * 2; // Qayta ishlangan elementni qaytaradi
}
return sum; // Asl ma'lumotlar yig'indisini qaytaradi
}
function* analyzePipeline(rawData) {
console.log("Starting data processing...");
// yield* processData ning qaytariladigan qiymatini oladi
const totalSum = yield* processData(rawData);
console.log(`Original data sum: ${totalSum}`);
yield "Processing complete!";
return `Final sum reported: ${totalSum}`;
}
const pipeline = analyzePipeline([10, 20, 30]);
let result = pipeline.next();
while (!result.done) {
console.log(`Pipeline output: ${result.value}`);
result = pipeline.next();
}
console.log(`Final pipeline result: ${result.value}`);
// Kutilayotgan Natija:
// Starting data processing...
// Pipeline output: 20
// Pipeline output: 40
// Pipeline output: 60
// Original data sum: 60
// Pipeline output: Processing complete!
// Final pipeline result: Final sum reported: 60
Bu yerda processData nafaqat o'zgartirilgan qiymatlarni qaytaradi, balki asl ma'lumotlar yig'indisini ham qaytaradi. analyzePipeline o'zgartirilgan qiymatlarni iste'mol qilish uchun yield* dan foydalanadi va bir vaqtning o'zida ushbu yig'indini oladi, bu esa delegatsiya qiluvchi generatorga delegatsiya qilingan operatsiyaning yakuniy natijasiga javob berish yoki undan foydalanish imkonini beradi.
Ilg'or Foydalanish Holati: Daraxtni Kesish
yield* daraxtlar kabi rekursiv tuzilmalar uchun juda yaxshi.
class TreeNode {
constructor(value) {
this.value = value;
this.children = [];
}
addChild(node) {
this.children.push(node);
}
// Daraxtni chuqurlik bo'yicha kesish uchun tugunni iterable qilish
*[Symbol.iterator]() {
yield this.value; // Joriy tugun qiymatini qaytaradi
for (const child of this.children) {
yield* child; // Bolalarni o'z kesishlari uchun delegatsiya qiladi
}
}
}
const root = new TreeNode('A');
const nodeB = new TreeNode('B');
const nodeC = new TreeNode('C');
const nodeD = new TreeNode('D');
const nodeE = new TreeNode('E');
root.addChild(nodeB);
root.addChild(nodeC);
nodeB.addChild(nodeD);
nodeC.addChild(nodeE);
console.log("Tree traversal (Depth-First):");
for (const val of root) {
console.log(val);
}
// Natija:
// Tree traversal (Depth-First):
// A
// B
// D
// C
// E
Bu yield* yordamida chuqurlik bo'yicha kesishni nafis tarzda amalga oshiradi, uning rekursiv iteratsiya shablonlari uchun kuchini ko'rsatadi.
2. Qiymatlarni Generatorga Yuborish: Argumentli next() Metodi
Generatorlar uchun eng ta'sirchan "protokol kengaytmalari" dan biri ularning ikki tomonlama aloqa qobiliyatidir. yield qiymatlarni generatordan chiqarib yuborsa, next() metodi ham argumentni qabul qilib, qiymatlarni to'xtatilgan generatorga qaytarishga imkon beradi. Bu generatorlarni oddiy ma'lumot ishlab chiqaruvchilardan to'xtash, kiritishni qabul qilish, qayta ishlash va qayta boshlash qobiliyatiga ega kuchli korutin-kabi konstruktsiyalarga aylantiradi.
Qanday ishlaydi
generatorObject.next(valueToInject) ni chaqirganingizda, valueToInject generatorni to'xtatib qo'ygan yield ifodasining natijasiga aylanadi. Agar generator yield tomonidan to'xtatilgan bo'lmasa (masalan, u endigina boshlangan bo'lsa yoki tugagan bo'lsa), kiritilgan qiymat e'tiborga olinmaydi.
function* interactiveProcess() {
const input1 = yield "Please provide the first number:";
console.log(`Received first number: ${input1}`);
const input2 = yield "Now, provide the second number:";
console.log(`Received second number: ${input2}`);
const sum = Number(input1) + Number(input2);
yield `The sum is: ${sum}`;
return "Process complete.";
}
const process = interactiveProcess();
// Birinchi next() chaqiruv generatorni ishga tushiradi, argument e'tiborga olinmaydi.
// U birinchi so'rovni qaytaradi.
let response = process.next();
console.log(response.value); // Please provide the first number:
// Birinchi raqamni generatorga qaytarish
response = process.next(10);
console.log(response.value); // Now, provide the second number:
// Ikkinchi raqamni qaytarish
response = process.next(20);
console.log(response.value); // The sum is: 30
// Jarayonni yakunlash
response = process.next();
console.log(response.value); // Process complete.
console.log(response.done); // true
Ushbu misol generatorning qanday to'xtashini, kiritishni so'rashini va keyin uni bajarishni davom ettirish uchun kiritishni qabul qilishini aniq ko'rsatadi. Bu murakkab interaktiv tizimlar, holat mashinalari va keyingi qadam tashqi fikr-mulohazalarga bog'liq bo'lgan yanada murakkab ma'lumotlar o'zgartirishlarini qurish uchun asosiy shablondir.
Ikki tomonlama Aloqa uchun Foydalanish Holatlari
- Korutinlar va Kooperativ Ko'p Vazifalik: Generatorlar yengil korutinlar vazifasini bajarishi mumkin, ixtiyoriy ravishda nazoratni beradi va ma'lumotlarni qabul qiladi, bu asosiy ish oqimini bloklamasdan murakkab holatni yoki uzoq davom etuvchi vazifalarni boshqarish uchun foydalidir (hodisalar silsilasi yoki
setTimeoutbilan birgalikda). - Holat Mashinalari: Generatorning ichki holati (lokal o'zgaruvchilar, dastur hisoblagichi)
yieldchaqiruvlari bo'yicha saqlanib qoladi, bu ularni tashqi kiritishlar orqali o'zgarishlar yuz beradigan holat mashinalarini modellashtirish uchun ideal qiladi. - Kiritish/Chiqarish (I/O) Simulyatsiyasi: Asinxron operatsiyalar yoki foydalanuvchi kiritishini simulyatsiya qilish uchun, argumentli
next()generator oqimini sinash va boshqarishning sinxron usulini ta'minlaydi. - Tashqi Konfiguratsiyali Ma'lumotlar O'zgartirish Quvurlari: Ayrim qayta ishlash bosqichlari bajarilish vaqtida dinamik tarzda aniqlanadigan parametrlarni talab qiladigan quvurni tasavvur qiling.
3. Generator Ob'ektlaridagi throw() va return() Metodlari
next() dan tashqari, generator ob'ektlari throw() va return() metodlarini ham ochib beradi, ular o'z bajarilish oqimini tashqaridan qo'shimcha nazorat qilish imkonini beradi. Bu metodlar tashqi kodga xatolarni kiritishga yoki erta tugatishni majbur qilishga imkon beradi, bu esa murakkab generatorga asoslangan tizimlarda xatolarni boshqarish va resurslarni boshqarishni sezilarli darajada yaxshilaydi.
generatorObject.throw(exception): Xatolarni Kiritish
generatorObject.throw(exception) ni chaqirish generatorga uning joriy to'xtatilgan holatida istisno kiritadi. Bu istisno generator tanasidagi throw bayonoti kabi harakat qiladi. Agar generator to'xtatilgan yield bayonoti atrofida try...catch blokiga ega bo'lsa, u bu tashqi xatoni ushlab, boshqarishi mumkin.
Agar generator istisnoga tutilmasa, u xuddi har qanday ishlanmagan istisno kabi throw() chaqiruvchisiga tarqaladi.
function* dataProcessor() {
try {
const data = yield "Waiting for data...";
console.log(`Processing: ${data}`);
if (typeof data !== 'number') {
throw new Error("Invalid data type: expected number.");
}
yield `Data processed: ${data * 2}`;
} catch (error) {
console.error(`Caught error inside generator: ${error.message}`);
return "Error handled and generator terminated."; // Generator xato yuz berganda qiymat qaytarishi mumkin
} finally {
console.log("Generator cleanup complete.");
}
}
const processor = dataProcessor();
console.log(processor.next().value); // Waiting for data...
// Generatorga tashqi xato tashlashni simulyatsiya qilish
console.log("Attempting to throw an error into the generator...");
let resultWithError = processor.throw(new Error("External interruption!"));
console.log(`Result after external error: ${resultWithError.value}`); // Error handled and generator terminated.
console.log(`Done after error: ${resultWithError.done}`); // true
console.log("\n--- Second attempt with valid data, then an internal type error ---");
const processor2 = dataProcessor();
console.log(processor2.next().value); // Waiting for data...
console.log(processor2.next(5).value); // Data processed: 10
// Endi noto'g'ri ma'lumot yuboring, bu ichki tashlashga olib keladi
let resultInvalidData = processor2.next("abc");
// Generator o'zining tashlashini ushlaydi
console.log(`Result after invalid data: ${resultInvalidData.value}`); // Error handled and generator terminated.
console.log(`Done after error: ${resultInvalidData.done}`); // true
throw() metodi tashqi hodisalar silsilasi yoki promise zanjiridan xatolarni generatorga qaytarish, generatorlar tomonidan boshqariladigan asinxron operatsiyalar bo'yicha yagona xatolarni boshqarishni ta'minlash uchun juda qimmatlidir.
generatorObject.return(value): Majburiy Tugatish
generatorObject.return(value) metodi generatorni muddatidan oldin tugatishga imkon beradi. Chaqirilganda, generator darhol tugaydi va uning next() metodi keyinchalik { value: value, done: true } ni qaytaradi (yoki { value: undefined, done: true } agar value berilmagan bo'lsa). Generator ichidagi har qanday finally bloklari hali ham bajariladi, bu to'g'ri tozalashni ta'minlaydi.
function* resourceIntensiveOperation() {
try {
let count = 0;
while (true) {
yield `Processing item ${++count}`;
// Ba'zi og'ir ishni simulyatsiya qiling
if (count > 50) { // Xavfsizlik tanaffusi
return "Processed many items, returning.";
}
}
} finally {
console.log("Resource cleanup for intensive operation.");
}
}
const op = resourceIntensiveOperation();
console.log(op.next().value); // Processing item 1
console.log(op.next().value); // Processing item 2
console.log(op.next().value); // Processing item 3
// Erta to'xtatishga qaror qilindi
console.log("External decision: terminating operation early.");
let finalResult = op.return("Operation cancelled by user.");
console.log(`Final result after termination: ${finalResult.value}`); // Operation cancelled by user.
console.log(`Done: ${finalResult.done}`); // true
// Keyingi chaqiruvlar u tugaganligini ko'rsatadi
console.log(op.next()); // { value: undefined, done: true }
Bu uzoq davom etuvchi yoki resurslarni ko'p talab qiladigan iterativ jarayonni foydalanuvchi bekor qilishi yoki ma'lum bir chegaraga yetish kabi tashqi shartlar tufayli muloyimlik bilan to'xtatish kerak bo'lgan stsenariylar uchun juda foydalidir. finally bloki ajratilgan resurslarning to'g'ri bo'shatilishini ta'minlaydi, bu esa sizib chiqishlarning oldini oladi.
Ilg'or Shablonlar va Global Foydalanish Holatlari
Generator protokoli kengaytmalar zamonaviy JavaScriptdagi eng kuchli shablonlarning, ayniqsa asinxronlikni va murakkab ma'lumotlar oqimlarini boshqarishda asos bo'lib xizmat qiladi. Asosiy tushunchalar global miqyosda bir xil bo'lib qolsa-da, ularning qo'llanilishi turli xalqaro loyihalar bo'yicha rivojlanishni sezilarli darajada soddalashtirishi mumkin.
Asinxron Generatorlar va for await...of bilan Asinxron Iteratsiya
Iterator va generator protokollariga asoslanib, ECMAScript Asinxron Generatorlarni va for await...of siklini taqdim etdi. Bular asinxron ma'lumotlar manbalari bo'yicha iteratsiya qilishning sinxron ko'rinishli usulini ta'minlaydi, va'dalar oqimlarini yoki tarmoq javoblarini oddiy massivlar kabi ko'rib chiqadi.
Asinxron Iterator Protokoli
Sinxron analoglari singari, asinxron iterables ham [Symbol.asyncIterator] metodiga ega bo'lib, u asinxron iteratorni qaytaradi. Asinxron iterator async next() metodiga ega bo'lib, u { value: ..., done: ... } ob'ektiga yechiladigan promise qaytaradi.
Asinxron Generator Funksiyalari (async function*)
async function* avtomatik ravishda asinxron iteratorni qaytaradi. Siz ularning tanasida promise-lar uchun bajarishni to'xtatish uchun await dan va qiymatlarni asinxron tarzda ishlab chiqarish uchun yield dan foydalanasiz.
async function* fetchPaginatedData(url) {
let nextPage = url;
while (nextPage) {
const response = await fetch(nextPage);
const data = await response.json();
yield data.results; // Joriy sahifadagi natijalarni qaytaradi
// API keyingi sahifa URL manzilini ko'rsatadi deb faraz qiling
nextPage = data.next_page_url;
if (nextPage) {
console.log(`Fetching next page: ${nextPage}`);
}
await new Promise(resolve => setTimeout(resolve, 100)); // Keyingi chaqiruv uchun tarmoq kechikishini simulyatsiya qilish
}
return "All pages fetched.";
}
// Foydalanish misoli:
async function processAllData() {
console.log("Starting data fetching...");
try {
for await (const pageResults of fetchPaginatedData("https://api.example.com/items?page=1")) {
console.log("Processed a page of results:", pageResults.length, "items.");
// Bu yerda har bir sahifadagi ma'lumotlarni qayta ishlashni tasavvur qiling
// masalan, ma'lumotlar bazasiga saqlash, ko'rsatish uchun o'zgartirish
for (const item of pageResults) {
console.log(` - Item ID: ${item.id}`);
}
}
console.log("Finished all data fetching and processing.");
} catch (error) {
console.error("An error occurred during data fetching:", error.message);
}
}
// Haqiqiy ilovada, dummy URL yoki mock fetch bilan almashtiring
// Ushbu misol uchun, shunchaki tuzilmani o'rinbosar bilan ko'rsataylik:
// (Eslatma: `fetch` va haqiqiy URL manzillar brauzer yoki Node.js muhitini talab qiladi)
// await processAllData(); // Buni asinxron kontekstda chaqiring
Bu shablon har qanday asinxron operatsiyalar ketma-ketligini boshqarish uchun juda kuchli bo'lib, siz butun oqim tugashini kutmasdan elementlarni birma-bir qayta ishlashni xohlaysiz. Quyidagilarni o'ylab ko'ring:
- Katta fayllarni yoki tarmoq oqimlarini bo'lak-bo'lak o'qish.
- Sahifalangan API'lardan ma'lumotlarni samarali qayta ishlash.
- Haqiqiy vaqt rejimida ma'lumotlarni qayta ishlash quvurlarini qurish.
Global miqyosda bu yondashuv ishlab chiquvchilarning asinxron ma'lumotlar oqimlarini qanday iste'mol qilishi va ishlab chiqarishini standartlashtiradi, turli backend va frontend muhitlarida izchillikni oshiradi.
Generatorlar Holat Mashinalari va Korutinlar Sifatida
Generatorlarning to'xtash va qayta boshlash qobiliyati, ikki tomonlama aloqa bilan birgalikda, ularni aniq holat mashinalari yoki yengil korutinlar qurish uchun ajoyib vosita qiladi.
function* vendingMachine() {
let balance = 0;
yield "Welcome! Insert coins (values: 1, 2, 5).";
while (true) {
const coin = yield `Current balance: ${balance}. Waiting for coin or "buy".`;
if (coin === "buy") {
if (balance >= 5) { // Element narxi 5 deb faraz qiling
balance -= 5;
yield `Here is your item! Change: ${balance}.`;
} else {
yield `Insufficient funds. Need ${5 - balance} more.`;
}
} else if ([1, 2, 5].includes(Number(coin))) {
balance += Number(coin);
yield `Inserted ${coin}. New balance: ${balance}.`;
} else {
yield "Invalid input. Please insert 1, 2, 5, or 'buy'.";
}
}
}
const machine = vendingMachine();
console.log(machine.next().value); // Welcome! Insert coins (values: 1, 2, 5).
console.log(machine.next().value); // Current balance: 0. Waiting for coin or "buy".
console.log(machine.next(2).value); // Inserted 2. New balance: 2.
console.log(machine.next(5).value); // Inserted 5. New balance: 7.
console.log(machine.next("buy").value); // Here is your item! Change: 2.
console.log(machine.next("buy").value); // Current balance: 2. Waiting for coin or "buy".
console.log(machine.next("exit").value); // Invalid input. Please insert 1, 2, 5, or 'buy'.
Ushbu savdo avtomati misoli generatorning ichki holatni (balance) qanday saqlashi va tashqi kiritishga (coin yoki "buy") asoslanib holatlar orasida qanday o'tishini ko'rsatadi. Bu shablon o'yin sikllari, UI yordamchilari yoki aniq belgilangan ketma-ket qadamlar va o'zaro ta'sirlarga ega har qanday jarayon uchun juda qimmatlidir.
Moslashuvchan Ma'lumotlar O'zgartirish Quvurlarini Qurish
Generatorlar, ayniqsa yield* bilan, kompozitsiyaga qodir ma'lumotlar o'zgartirish quvurlarini yaratish uchun mukammaldir. Har bir generator qayta ishlash bosqichini ifodalashi mumkin va ular bir-biriga bog'lanishi mumkin.
function* filterEvens(numbers) {
for (const num of numbers) {
if (num % 2 === 0) {
yield num;
}
}
}
function* doubleValues(numbers) {
for (const num of numbers) {
yield num * 2;
}
}
function* sumUpTo(numbers, limit) {
let sum = 0;
for (const num of numbers) {
if (sum + num > limit) {
return sum; // Agar keyingi raqamni qo'shish chegaradan oshsa, to'xtating
}
sum += num;
yield sum; // Yig'indini qaytaradi
}
return sum;
}
// Quvurlarni boshqarish generatori
function* dataPipeline(data) {
console.log("Pipeline Stage 1: Filtering even numbers...");
// Bu yerda `yield*` iteratsiya qiladi, u filterEvens dan qaytariladigan qiymatni olmaydi
// agar filterEvens aniq bir qiymatni qaytarmasa (standart bo'yicha qaytarmaydi).
// Haqiqiy kompozitsiyaga qodir quvurlar uchun, har bir bosqich yangi generator yoki iterableni to'g'ridan-to'g'ri qaytarishi kerak.
// Generatorlarni to'g'ridan-to'g'ri bog'lash ko'pincha funktsionalroq bo'ladi:
const filteredAndDoubled = doubleValues(filterEvens(data));
console.log("Pipeline Stage 2: Summing up to a limit (100)...");
const finalSum = yield* sumUpTo(filteredAndDoubled, 100);
return `Final sum within limit: ${finalSum}`;
}
const rawData = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20];
const pipelineExecutor = dataPipeline(rawData);
let pipelineResult = pipelineExecutor.next();
while (!pipelineResult.done) {
console.log(`Intermediate pipeline output: ${pipelineResult.value}`);
pipelineResult = pipelineExecutor.next();
}
console.log(pipelineResult.value);
// To'g'rilangan bog'lash usuli (to'g'ridan-to'g'ri funktsional kompozitsiya):
console.log("\n--- Direct Chaining Example (Functional Composition) ---");
const processedNumbers = doubleValues(filterEvens(rawData)); // Iterablesni bog'lash
let cumulativeSumIterator = sumUpTo(processedNumbers, 100); // Oxirgi bosqichdan iterator yaratish
for (const val of cumulativeSumIterator) {
console.log(`Cumulative Sum: ${val}`);
}
// sumUpTo ning yakuniy qaytariladigan qiymati (agar for...of tomonidan ishlatilmasa) .return() yoki .next() orqali done bo'lgandan keyin olinadi
console.log(`Final cumulative sum (from iterator's return value): ${cumulativeSumIterator.next().value}`);
// Kutilayotgan natija filtrlangan, keyin ikki barobarga oshirilgan juft sonlarni, keyin ularning 100 gacha bo'lgan kumulyativ yig'indisini ko'rsatadi.
// rawData [1,2,3...20] uchun filterEvens -> doubleValues -> sumUpTo(..., 100) tomonidan qayta ishlangan misol ketma-ketligi:
// Filtrlangan juft sonlar: [2, 4, 6, 8, 10, 12, 14, 16, 18, 20]
// Ikki barobarga oshirilgan juft sonlar: [4, 8, 12, 16, 20, 24, 28, 32, 36, 40]
// 100 gacha kumulyativ yig'indi:
// Sum: 4
// Sum: 12 (4+8)
// Sum: 24 (12+12)
// Sum: 40 (24+16)
// Sum: 60 (40+20)
// Sum: 84 (60+24)
// Yakuniy kumulyativ yig'indi (iteratorning qaytariladigan qiymatidan): 84 (chunki 28 ni qo'shish 100 dan oshib ketadi)
To'g'rilangan bog'lash misoli funksional kompozitsiyaning generatorlar tomonidan qanday tabiiy ravishda osonlashtirilishini ko'rsatadi. Har bir generator iterable (yoki boshqa generator) olib, yangi iterable ishlab chiqaradi, bu esa yuqori moslashuvchan va samarali ma'lumotlarni qayta ishlashga imkon beradi. Bu yondashuv turli sohalarda, global miqyosda katta ma'lumotlar to'plamlari yoki murakkab analitik ish oqimlari bilan shug'ullanadigan muhitlarda yuqori baholanadi.
Generatorlardan Foydalanishning Eng Yaxshi Amaliyotlari
Generatorlar va ularning protokol kengaytmalaridan samarali foydalanish uchun quyidagi eng yaxshi amaliyotlarni ko'rib chiqing:
- Generatorlarni Fokuslangan Tutish: Har bir generator ideal ravishda bitta, aniq belgilangan vazifani bajarishi kerak (masalan, filtrlash, xaritalash, sahifa olish). Bu qayta ishlatish imkoniyatini va sinovchanlikni oshiradi.
- Aniq Nomlash Konvensiyalari: Generator funksiyalari va ular
yieldqiladigan qiymatlar uchun tushunarli nomlardan foydalaning. Masalan,fetchUsersPage()yokiprocessCsvRows(). - Xatolarni Muloyimlik Bilan Boshqarish: Generatorlar ichida
try...catchbloklaridan foydalaning va, ayniqsa asinxron kontekstlarda, xatolarni samarali boshqarish uchun tashqi koddangeneratorObject.throw()ni ishlatishga tayyor bo'ling. finallybilan Resurslarni Boshqarish: Agar generator resurslarni olgan bo'lsa (masalan, fayl tutqichini ochish, tarmoq ulanishini o'rnatish), ushbu resurslarreturn()yoki ishlanmagan istisno orqali generator muddatidan oldin tugasa ham, bo'shatilishini ta'minlash uchunfinallyblokidan foydalaning.- Kompozitsiya uchun
yield*ni afzal ko'ring: Bir nechta iterables yoki generatorlarning chiqishini birlashtirganda,yield*delegatsiya qilishning eng toza va samarali usuli bo'lib, kodingizni modulli va tushunarli qiladi. - Ikki tomonlama Aloqani Tushunish: Argumentlar bilan
next()dan foydalanganda ataylab bo'ling. U kuchli, ammo agar oqilona ishlatilmasa, generatorlarni tushunishni qiyinlashtirishi mumkin. Kiritishlar qachon kutilishini aniq hujjatlashtiring. - Ishlashni Ko'rib Chiqing: Generatorlar samarali bo'lsa-da, ayniqsa dangasa baholash uchun, juda chuqur
yield*delegatsiya zanjirlari yoki ishlash uchun muhim sikllarda juda tez-teznext()chaqiruvlaridan ehtiyot bo'ling. Zarur bo'lsa, profil tuzing. - Sinxron Sinovdan O'tkazish: Generatorlarni boshqa har qanday funksiya kabi sinovdan o'tkazing. Berilgan qiymatlar ketma-ketligini, qaytariladigan qiymatni va ularning
throw()yokireturn()chaqirilganda qanday ishlashini tekshiring.
Zamonaviy JavaScript Rivojlanishiga Ta'siri
Generator protokoli kengaytmalar JavaScript evolyutsiyasiga katta ta'sir ko'rsatdi:
- Asinxron Kodni Sodda Lashtirish:
async/awaitdan oldin,cokabi kutubxonalar bilan generatorlar sinxron ko'rinadigan asinxron kodni yozishning asosiy mexanizmi edi. Ular biz bugun foydalanadiganasync/awaitsintaksisi uchun yo'l ochdi, bu ichki ravishda ko'pincha bajarishni to'xtatish va qayta boshlashning shunga o'xshash tushunchalaridan foydalanadi. - Kengaytirilgan Ma'lumotlar Oqimi va Qayta Ishlash: Generatorlar katta ma'lumotlar to'plamlarini yoki cheksiz ketma-ketliklarni dangasa tarzda qayta ishlashda ustundir. Bu shuni anglatadiki, ma'lumotlar hamma narsani bir vaqtning o'zida xotiraga yuklash o'rniga, talab bo'yicha qayta ishlanadi, bu veb-ilovalar, server-taraf Node.js va ma'lumotlar tahlil qilish vositalarida ishlash va masshtablanish uchun juda muhimdir.
- Funktsional Shablonlarni Rivojlantirish: Iterables va iteratorlarni yaratishning tabiiy usulini ta'minlab, generatorlar ko'proq funktsional dasturlash paradigmalarini osonlashtiradi, ma'lumotlar o'zgartirishlarining nafis kompozitsiyasini ta'minlaydi.
- Mustahkam Boshqaruv Oqimini Qurish: Ularning to'xtash, qayta boshlash, kiritishni qabul qilish va xatolarni boshqarish qobiliyati ularni murakkab boshqaruv oqimlari, holat mashinalari va hodisalar tomonidan boshqariladigan arxitekturalarni amalga oshirish uchun ko'p qirrali vosita qiladi.
Haqiqiy vaqt rejimida ma'lumotlar tahlil qilish platformalaridan tortib interaktiv veb-tajribalargacha bo'lgan loyihalarda turli xil jamoalar hamkorlik qiladigan tobora o'zaro bog'langan global rivojlanish landshaftida generatorlar murakkab muammolarni aniqlik va samaradorlik bilan hal qilish uchun umumiy, kuchli til xususiyatini taklif qiladi. Ularning universal qo'llanilishi dunyo bo'ylab har qanday JavaScript dasturchisi uchun qimmatli ko'nikma hisoblanadi.
Xulosa: Iteratsiyaning To'liq Potentsialini Ochish
JavaScript Generatorlar, ularning kengaytirilgan protokoli bilan, iteratsiya, asinxron operatsiyalar va murakkab boshqaruv oqimlarini boshqarishda sezilarli sakrashni anglatadi. yield* tomonidan taqdim etilgan nafis delegatsiyadan tortib, next() argumentlari orqali kuchli ikki tomonlama aloqaga, va throw() va return() bilan mustahkam xatolarni/tugatishni boshqarishgacha, bu xususiyatlar ishlab chiquvchilarga misli ko'rilmagan darajada nazorat va moslashuvchanlikni ta'minlaydi.
Ushbu takomillashtirilgan iterator interfeyslarini tushunib va o'zlashtirib, siz shunchaki yangi sintaksisni o'rganmayapsiz; siz yanada samarali, o'qilishi mumkin va saqlanishi oson kod yozish uchun vositalarni qo'lga kiritasiz. Siz murakkab ma'lumotlar quvurlarini quryapsizmi, murakkab holat mashinalarini amalga oshiryapsizmi yoki asinxron operatsiyalarni soddalashtiryapsizmi, generatorlar kuchli va idiomatik yechimni taklif qiladi.
Takomillashtirilgan iterator interfeysini qabul qiling. Uning imkoniyatlarini o'rganing. Sizning JavaScript kodingiz - va sizning loyihalaringiz - shu tufayli yaxshiroq bo'ladi.