खोज…
परिचय
जनरेटर फ़ंक्शन ( function*
कीवर्ड द्वारा परिभाषित) को कोरटाइन के रूप में चलाते हैं, मानों की एक श्रृंखला उत्पन्न करते हुए जैसे कि वे एक पुनरावृत्त के माध्यम से अनुरोध करते हैं।
वाक्य - विन्यास
- समारोह * नाम (पैरामीटर) {उपज मूल्य; प्रतिलाभ की मात्रा }
- जनरेटर = नाम (तर्क)
- {मान, किया}} = generator.next (मूल्य)
- {मान, किया गया} = जनरेटर। ग्रेट (मूल्य)
- generator.throw (त्रुटि)
टिप्पणियों
जेनरेटर फ़ंक्शन ईएस 2015 विनिर्देश के भाग के रूप में शुरू की गई एक विशेषता है और सभी ब्राउज़रों में उपलब्ध नहीं हैं। वे v6.0
रूप में Node.js में भी पूरी तरह से समर्थित हैं। एक विस्तृत ब्राउज़र संगतता सूची के लिए, MDN प्रलेखन और नोड के लिए, नोड.ग्रीन वेबसाइट देखें।
जेनरेटर के कार्य
एक जनरेटर फ़ंक्शन एक function*
घोषणा के साथ बनाया जाता है। जब इसे बुलाया जाता है, तो इसके शरीर को तुरंत निष्पादित नहीं किया जाता है। इसके बजाय, यह एक जनरेटर ऑब्जेक्ट लौटाता है, जिसका उपयोग फ़ंक्शन के निष्पादन के माध्यम से "कदम" करने के लिए किया जा सकता है।
फ़ंक्शन बॉडी के अंदर एक yield
अभिव्यक्ति एक बिंदु को परिभाषित करता है जिस पर निष्पादन निलंबित और फिर से शुरू हो सकता है।
function* nums() {
console.log('starting'); // A
yield 1; // B
console.log('yielded 1'); // C
yield 2; // D
console.log('yielded 2'); // E
yield 3; // F
console.log('yielded 3'); // G
}
var generator = nums(); // Returns the iterator. No code in nums is executed
generator.next(); // Executes lines A,B returning { value: 1, done: false }
// console: "starting"
generator.next(); // Executes lines C,D returning { value: 2, done: false }
// console: "yielded 1"
generator.next(); // Executes lines E,F returning { value: 3, done: false }
// console: "yielded 2"
generator.next(); // Executes line G returning { value: undefined, done: true }
// console: "yielded 3"
प्रारंभिक पुनरावृत्ति निकास
generator = nums();
generator.next(); // Executes lines A,B returning { value: 1, done: false }
generator.next(); // Executes lines C,D returning { value: 2, done: false }
generator.return(3); // no code is executed returns { value: 3, done: true }
// any further calls will return done = true
generator.next(); // no code executed returns { value: undefined, done: true }
जनरेटर फ़ंक्शन में त्रुटि फेंकना
function* nums() {
try {
yield 1; // A
yield 2; // B
yield 3; // C
} catch (e) {
console.log(e.message); // D
}
}
var generator = nums();
generator.next(); // Executes line A returning { value: 1, done: false }
generator.next(); // Executes line B returning { value: 2, done: false }
generator.throw(new Error("Error!!")); // Executes line D returning { value: undefined, done: true}
// console: "Error!!"
generator.next(); // no code executed. returns { value: undefined, done: true }
यात्रा
एक जनरेटर चलने योग्य है । यह कथन के for...of
साथ खत्म किया जा सकता है, और अन्य निर्माणों में उपयोग किया जाता है जो पुनरावृत्ति प्रोटोकॉल पर निर्भर करते हैं।
function* range(n) {
for (let i = 0; i < n; ++i) {
yield i;
}
}
// looping
for (let n of range(10)) {
// n takes on the values 0, 1, ... 9
}
// spread operator
let nums = [...range(3)]; // [0, 1, 2]
let max = Math.max(...range(100)); // 99
यहाँ ES6 में कस्टम iterable ऑब्जेक्ट के लिए उपयोग जनरेटर का एक और उदाहरण है। यहां अनाम जनरेटर फ़ंक्शन function *
उपयोग किया जाता है।
let user = {
name: "sam", totalReplies: 17, isBlocked: false
};
user[Symbol.iterator] = function *(){
let properties = Object.keys(this);
let count = 0;
let isDone = false;
for(let p of properties){
yield this[p];
}
};
for(let p of user){
console.log( p );
}
जेनरेटर को मान भेजना
जनरेटर को next()
विधि से पारित करके एक मूल्य भेजना संभव है।
function* summer() {
let sum = 0, value;
while (true) {
// receive sent value
value = yield;
if (value === null) break;
// aggregate values
sum += value;
}
return sum;
}
let generator = summer();
// proceed until the first "yield" expression, ignoring the "value" argument
generator.next();
// from this point on, the generator aggregates values until we send "null"
generator.next(1);
generator.next(10);
generator.next(100);
// close the generator and collect the result
let sum = generator.next(null).value; // 111
अन्य जनरेटर के लिए प्रतिनिधि
एक जनरेटर फ़ंक्शन के भीतर से, yield*
का उपयोग करके नियंत्रण को दूसरे जनरेटर फ़ंक्शन को सौंपा जा सकता है।
function* g1() {
yield 2;
yield 3;
yield 4;
}
function* g2() {
yield 1;
yield* g1();
yield 5;
}
var it = g2();
console.log(it.next()); // 1
console.log(it.next()); // 2
console.log(it.next()); // 3
console.log(it.next()); // 4
console.log(it.next()); // 5
console.log(it.next()); // undefined
Iterator-Observer इंटरफ़ेस
एक जनरेटर दो चीजों का एक संयोजन है - एक Iterator
और एक Observer
।
इटरेटर
एक iterable
एक चीज है जब आह्वान एक iterable
रिटर्न देता है। एक iterable
एक ऐसी चीज है जिस पर आप पुनरावृत्ति कर सकते हैं। ES6 / ES2015 के बाद से, सभी संग्रह (Array, Map, Set, WeakMap, WeakSet) Iterable अनुबंध के अनुरूप हैं।
एक जनरेटर (पुनरावृत्त) एक निर्माता है। यात्रा में उपभोक्ता
PULL
निर्माता से मूल्य है।
उदाहरण:
function *gen() { yield 5; yield 6; }
let a = gen();
जब भी आप a.next()
कॉल करते हैं, तो आप अनिवार्य रूप से Iterator से मूल्य pull
और yield
पर निष्पादन pause
। अगली बार जब आप a.next()
, तो निष्पादन पहले से रुकी हुई स्थिति से फिर से शुरू होता है।
देखने वाला
एक जनरेटर भी एक पर्यवेक्षक है जिसका उपयोग करके आप जनरेटर में कुछ मान वापस भेज सकते हैं।
function *gen() {
document.write('<br>observer:', yield 1);
}
var a = gen();
var i = a.next();
while(!i.done) {
document.write('<br>iterator:', i.value);
i = a.next(100);
}
यहां आप देख सकते हैं कि yield 1
का उपयोग एक अभिव्यक्ति की तरह किया जाता है जो कुछ मूल्य का मूल्यांकन करता है। मान जिसका मूल्यांकन करता है वह मान है जिसे a.next
फ़ंक्शन कॉल के तर्क के रूप में भेजा जाता है।
तो, पहली बार i.value
पहली वैल्यू i.value
( 1
) होगी, और जब अगली स्टेट के लिए पुनरावृत्ति जारी रहेगी, तो हम a.next(100)
का उपयोग करके जनरेटर को वापस वैल्यू भेजते हैं।
जेनरेटर के साथ async करना
जेनरेटर व्यापक रूप से spawn
(टास्कज या को-फंक्शन से) फ़ंक्शन के साथ उपयोग किया जाता है, जहां फ़ंक्शन एक जनरेटर में लेता है और हमें सिंक्रोनस कोड को सिंक्रोनस फैशन में लिखने की अनुमति देता है। इसका मतलब यह नहीं है कि async कोड सिंक कोड में बदल जाता है / सिंक्रोनाइज़ किया जाता है। इसका मतलब है कि हम ऐसा कोड लिख सकते हैं जो sync
की तरह दिखता है लेकिन आंतरिक रूप से यह अभी भी async
।
सिंक BLOCKING है; Async WAITING है। कोड लिखना जो ब्लॉक करना आसान है। जब पूलिंग, मान असाइनमेंट स्थिति में दिखाई देता है। जब पुशिंग, मान कॉलबैक के तर्क स्थिति में दिखाई देता है।
आप iterators का उपयोग करते हैं, तो आप PULL
निर्माता से मूल्य। जब आप कॉलबैक का उपयोग करते हैं, तो निर्माता PUSH
कॉलबैक के तर्क स्थिति के लिए मूल्य से बच जाता है।
var i = a.next() // PULL
dosomething(..., v => {...}) // PUSH
यहां, आप a.next()
से मान a.next()
और दूसरे में, v => {...}
कॉलबैक है और कॉलबैक फ़ंक्शन के तर्क स्थिति v
में PUSH
एड है।
इस पुल-पुश तंत्र का उपयोग करते हुए, हम इस तरह से async प्रोग्रामिंग लिख सकते हैं,
let delay = t => new Promise(r => setTimeout(r, t));
spawn(function*() {
// wait for 100 ms and send 1
let x = yield delay(100).then(() => 1);
console.log(x); // 1
// wait for 100 ms and send 2
let y = yield delay(100).then(() => 2);
console.log(y); // 2
});
इसलिए, उपरोक्त कोड को देखते हुए, हम async कोड लिख रहे हैं जो दिखता है कि यह blocking
(पैदावार बयान 100ms की प्रतीक्षा करते हैं और फिर निष्पादन जारी रखते हैं), लेकिन यह वास्तव में waiting
। जनरेटर की pause
और resume
संपत्ति हमें इस अद्भुत चाल को करने की अनुमति देती है।
यह कैसे काम करता है ?
स्पॉन फ़ंक्शन जनरेटर से वादे की स्थिति को yield promise
करने के लिए yield promise
का उपयोग करता है, वादे के हल होने तक इंतजार करता है, और हल किए गए मूल्य को जनरेटर को वापस करता है ताकि वह इसका उपभोग कर सके।
इसे अब प्रयोग करो
तो, जनरेटर और स्पॉन फ़ंक्शन के साथ, आप NodeJS में अपने सभी async कोड को साफ करने के लिए देख सकते हैं और ऐसा महसूस कर सकते हैं कि यह तुल्यकालिक है। यह डिबगिंग को आसान बना देगा। साथ ही कोड साफ-सुथरा दिखेगा।
यह सुविधा जावास्क्रिप्ट के भविष्य के संस्करणों में आ रही है - async...await
। लेकिन आप पुस्तकालयों में परिभाषित स्पॉन फ़ंक्शन का उपयोग करके ES2015 / ES6 में आज उनका उपयोग कर सकते हैं - टास्क, सह, या ब्लूबर्ड
जनरेटर के साथ Async फ्लो
जेनरेटर ऐसे कार्य हैं, जो क्रियान्वयन को रोकने और फिर से शुरू करने में सक्षम हैं। यह बाहरी पुस्तकालयों, मुख्य रूप से q या सह का उपयोग करके async कार्यों का अनुकरण करने की अनुमति देता है। मूल रूप से यह उन कार्यों को लिखने की अनुमति देता है जो जाने के लिए async परिणामों की प्रतीक्षा करते हैं:
function someAsyncResult() {
return Promise.resolve('newValue')
}
q.spawn(function * () {
var result = yield someAsyncResult()
console.log(result) // 'newValue'
})
यह async कोड लिखने की अनुमति देता है जैसे कि यह सिंक्रोनस था। इसके अलावा, कई async ब्लॉक पर काम करने की कोशिश करें और पकड़ें। यदि वादे को अस्वीकार कर दिया जाता है, तो त्रुटि अगले कैच द्वारा पकड़ी जाती है:
function asyncError() {
return new Promise(function (resolve, reject) {
setTimeout(function () {
reject(new Error('Something went wrong'))
}, 100)
})
}
q.spawn(function * () {
try {
var result = yield asyncError()
} catch (e) {
console.error(e) // Something went wrong
}
})
सह का उपयोग करना बिल्कुल वैसा ही काम करेगा लेकिन q.spawn
बजाय co(function * (){...})
के साथ q.spawn