1. प्रश्न और संदर्भ
आपको एक सिंक्रोनस इटरेटर को प्रोसेस करना है जिसमें लाखों रिकॉर्ड हो सकते हैं: अमान्य पंक्तियों को फ़िल्टर करना, उन्हें व्यू मॉडल में मैप करना, पहले 100 रिकॉर्ड लेना, और कुल राशि की गणना करना। मूल कोड ने map, filter, और reduce को कॉल करने से पहले स्रोत को एक ऐरे में स्प्रेड (spread) कर दिया था, जिससे मेमोरी का बहुत अधिक पीक (peak) हुआ। इसे JavaScript Iterator Helpers के साथ फिर से लिखें और लेज़ीनेस, इटरेटर प्रोटोकॉल, समय से पहले समाप्ति, क्लीनअप, और पुराने रनटाइम फ़ॉलबैक की व्याख्या करें।
2. इंटरव्यूअर क्या मूल्यांकन करता है
- क्या आप Iterator और Iterable के बीच के अंतर को समझते हैं, और यह जानते हैं कि Iterator Helpers ऐसे इटरेटर लौटाते हैं जिनका आगे उपभोग जारी रखा जा सकता है।
- क्या आप map, filter, take, find, reduce, और toArray को एक लेज़ी पाइपलाइन में संयोजित कर सकते हैं।
- क्या आप जानते हैं कि एक इटरेटर स्टेटफुल होता है और आमतौर पर केवल एक बार आगे बढ़ता है, और समय से पहले समाप्ति अंतर्निहित return मेथड को क्लीनअप करने का अवसर देती है।
- क्या आप अनंत या महंगे स्रोतों, अपवादों (exceptions), कम्पैटिबिलिटी, और उस सीमा को संभालते हैं जहाँ ऐरे निर्माण उपयुक्त होता है।
3. उत्तर देने से पहले पूछे जाने वाले स्पष्टीकरण
- क्या स्रोत एक सिंक्रोनस Iterator है, एक Iterable है, या एक एसिंक्रोनस पेज्ड API है?
- क्या परिणाम को एक ऐरे के रूप में लौटाया जाना चाहिए, या कॉलर इसे स्ट्रीम के रूप में उपभोग करना जारी रख सकता है?
- N परिणाम लेने के बाद, क्या नेटवर्क, फ़ाइल या डेटाबेस कर्सर को बंद किया जाना चाहिए?
- क्या लक्षित Node और ब्राउज़र संस्करण मूल (native) Iterator Helpers प्रदान करते हैं, और क्या पॉलीफ़िल की अनुमति है?
4. 30-सेकंड का उत्तर ढांचा
मैं Iterable को Iterator में सामान्यीकृत (normalize) करूँगा, फिर filter, map, और take को श्रृंखलाबद्ध (chain) करूँगा, और toArray को केवल उसी सीमा पर कॉल करूँगा जिसे वास्तव में एक ऐरे की आवश्यकता है। map और filter तुरंत ट्रैवर्स नहीं करते; reduce और toArray उपभोग शुरू करते हैं। एक बार जब take अपनी सीमा तक पहुँच जाता है, तो पाइपलाइन को डेटा खींचना बंद कर देना चाहिए और इटरेटर क्लोजिंग का उपयोग करना चाहिए ताकि संसाधन मुक्त हो सकें। इटरेटर स्टेटफुल होते हैं और उन्हें उपभोक्ताओं के बीच लापरवाही से साझा नहीं किया जाना चाहिए। पुराने रनटाइम समान सिमेंटिक्स वाले नियंत्रित पॉलीफ़िल या जनरेटर कार्यान्वयन का उपयोग कर सकते हैं, जिनका बड़े स्रोतों पर अपवादों, समय से पहले समाप्ति, और मेमोरी उपयोग के लिए परीक्षण किया गया हो।
5. चरण-दर-चरण विस्तृत उत्तर
चरण 1: Iterator और Iterable में अंतर समझें
एक Iterable एक Symbol.iterator मेथड प्रदान करता है जो एक Iterator उत्पन्न कर सकता है; एक Iterator, next प्रदान करता है और done तथा value लौटाता है। Iterator.from उस इनपुट को सामान्यीकृत करता है जो इटरेशन प्रोटोकॉल का पालन करता है। Iterator Helpers इटरेटर पर काम करते हैं और लेज़ी हेल्पर्स बनाते हैं; वे पूरे स्रोत को स्वचालित रूप से किसी ऐरे में कॉपी नहीं करते हैं।
चरण 2: एक लेज़ी map, filter, और take पाइपलाइन बनाएं
नीचे दिया गया फ़ंक्शन स्रोत को केवल तभी पढ़ता है जब परिणाम का उपभोग किया जा रहा हो। filter एक पंक्ति की जांच करता है, map इसे रूपांतरित करता है, और take अनुरोधित संख्या के बाद रुक जाता है ताकि असंबद्ध रिकॉर्ड न खींचे जाएं।
function topAmounts(source, limit) {
return Iterator.from(source)
.filter((row) => row.status === "paid")
.map((row) => ({ id: row.id, amount: row.cents / 100 }))
.take(limit);
}
const firstHundred = topAmounts(records(), 100).toArray();चरण 3: उपभोग के समय और वन-पास स्टेट को समझें
हेल्पर बनाने से कॉलबैक कॉल नहीं होते हैं। next को खींचने, या forEach, find, reduce, या toArray को कॉल करने से स्रोत मानों का उपभोग होता है। एक इटरेटर वर्तमान स्थिति को संग्रहीत करता है; पहला उपभोक्ता इसकी स्थिति बदल देता है और दूसरे उपभोक्ता को समाप्त (exhausted) इटरेटर मिल सकता है। उपभोग किए गए इंस्टेंस को साझा करने के बजाय स्वतंत्र परिणामों के लिए एक नया स्रोत इटरेटर बनाएं।
चरण 4: समय से पहले समाप्ति, return, और अपवादों को संभालें
take और find जैसे ऑपरेशन परिणाम मिलने के बाद रुक सकते हैं। यदि अंतर्निहित इटरेटर return प्रदान करता है, तो हेल्पर को पूरा होने या विफलता पर संसाधनों (जैसे फ़ाइल हैंडल या पेज कर्सर) को बंद करने का अवसर देना चाहिए। व्यावसायिक कोड को फिर भी finally में अपने संसाधनों को मुक्त करना चाहिए, और जब कोई कॉलबैक त्रुटि उत्पन्न करता है या कोई उपभोक्ता समय से पहले रुक जाता है, तो क्लीनअप को सत्यापित करना चाहिए।
चरण 5: ऐरे निर्माण की सीमा और फ़ॉलबैक चुनें
toArray शेष परिणामों को ऐरे में बदल देता है, इसलिए इसे केवल वहीं रखें जहाँ रैंडम एक्सेस, सीरियलाइज़ेशन, या बैच रेंडरिंग को ऐरे की आवश्यकता होती है। अनंत इटरेटर, बड़े पेज और महंगी गणनाओं को लेज़ी ही रखा जाना चाहिए। यदि रनटाइम में नेटिव Iterator Helpers का अभाव है, तो map, filter, और take के लिए एक नियंत्रित पॉलीफ़िल या जनरेटर रैपर का उपयोग करें। वन-पास उपभोग, समय से पहले समाप्ति और अपवाद प्रसार को बनाए रखें; गुप्त रूप से प्रत्येक स्रोत को ऐरे में न बदलें।
6. उच्च-गुणवत्ता वाला नमूना उत्तर
मैं Iterator.from के साथ इनपुट को सामान्यीकृत करूँगा, फिर filter, map, और take को चेन करूँगा, और toArray को केवल उसी आउटपुट सीमा पर कॉल करूँगा जिसे ऐरे की आवश्यकता है। हेल्पर कॉलबैक उपभोग के दौरान चलते हैं, इसलिए कोई बड़ा स्रोत पहले से विस्तारित नहीं होता है; take या find को डेटा खींचना बंद कर देना चाहिए और अंतर्निहित कर्सर को बंद होने की अनुमति देने के लिए return का उपयोग करना चाहिए। एक इटरेटर स्टेटफुल होता है और आमतौर पर केवल एक बार उपयोग के लिए होता है, इसलिए स्वतंत्र परिणामों के लिए नए स्रोत इटरेटर की आवश्यकता होती है। पुराने Node या ब्राउज़र के लिए, मैं समान लेज़ी और क्लोजिंग सिमेंटिक्स के साथ पॉलीफ़िल या जनरेटर कार्यान्वयन का उपयोग करूँगा, और अपवादों, समय से पहले रुकने, संसाधन रिलीज, और मेमोरी पीक का परीक्षण करूँगा।
7. सामान्य गलतियाँ
- पहले स्रोत को स्प्रेड करना → पूरा स्रोत ऐरे में बदल जाता है → toArray को केवल उसी सीमा पर रखें जिसे वास्तव में ऐरे की आवश्यकता है।
- यह मान लेना कि map बनाने से कॉलबैक निष्पादित होते हैं → सेटअप के दौरान साइड इफेक्ट्स दिखाई नहीं देते → समझाएं कि उपभोग डेटा खींचने को ट्रिगर करता है।
- एक ही इटरेटर का पुन: उपयोग करना → दूसरा परिणाम खाली या आंशिक होता है → प्रत्येक उपभोक्ता के लिए एक नया स्रोत बनाएं।
- take के बाद भी पेज का अनुरोध करना जारी रखना → नेटवर्क और संसाधन बर्बाद होते हैं → सत्यापित करें कि समय से पहले समाप्ति अंतर्निहित return को कॉल करती है।
- ऐसा पॉलीफ़िल जो केवल ऐरे परिणामों को कॉपी करता है → अनंत-स्रोत और अपवाद सिमेंटिक्स बदल जाते हैं → लेज़ीनेस, वन-पास स्टेट और अपवाद प्रसार को बनाए रखें।
8. फॉलो-अप प्रश्न और उत्तर
फॉलो-अप 1: Iterator Helpers और ऐरे मेथड्स के बीच मुख्य अंतर क्या है?
ऐरे मेथड्स पहले से बने ऐरे पर काम करते हैं और आमतौर पर तुरंत ट्रैवर्स करते हैं। Iterator Helpers एक इटरेटर लेते हैं और map, filter, और take जैसे लेज़ी रूपांतरण करते हैं, और केवल उन्हीं तत्वों को खींचते हैं जिनका उपभोक्ता अनुरोध करता है।
फॉलो-अप 2: आपको अभी भी toArray कब कॉल करना चाहिए?
इसे तब कॉल करें जब किसी सीमा को रैंडम इंडेक्सिंग, सीरियलाइज़ेशन, ऐसे बैच API की आवश्यकता हो जो केवल ऐरे स्वीकार करता हो, या एक छोटे वन-टाइम रेंडर की आवश्यकता हो। विशाल या अनंत स्रोतों के लिए ऐरे निर्माण से बचें और लेज़ी उपभोग जारी रखें।
फॉलो-अप 3: किसी इटरेटर का लापरवाही से पुन: उपयोग क्यों नहीं किया जाना चाहिए?
यह एक कर्सर संग्रहीत करता है, और next इसकी आंतरिक स्थिति को बदल देता है। एक उपभोक्ता द्वारा इसे पढ़ने के बाद, दूसरे उपभोक्ता को शेष स्थिति प्राप्त होती है। पुनः चलाने के लिए Iterable से निर्मित एक नए इटरेटर की आवश्यकता होती है।
फॉलो-अप 4: आप कैसे सत्यापित करते हैं कि समय से पहले समाप्ति संसाधनों को मुक्त करती है?
एक ऐसे टेस्ट इटरेटर का उपयोग करें जो डेटा खींचने की संख्या की गणना करता है और return कॉल्स को रिकॉर्ड करता है। take या find के बाद, पुष्टि करें कि डेटा खींचना बंद हो गया है और return चला है, फिर कॉलबैक अपवादों और उपभोक्ता रुकावट को कवर करें।