दिलचस्प पोस्ट
वेबकिट-ट्रांसफॉर्म क्रोम 13 में z- इंडेक्स ऑर्डरिंग को ओवरराइट करता है एक HTML तालिका पर स्क्रॉल बार कैसे प्रदर्शित करें स्विफ्ट के गार्ड कीवर्ड कैसे सी में extern कीवर्ड सही ढंग से उपयोग करने के लिए नेविगेशन ड्रॉवर में एक चेक किए गए मेनू आइटम का रंग बदलें सी # यादृच्छिक संख्या जेनरेटर थ्रेड सुरक्षित है? सी में एकल वर्ण के लिए scanf कैसे करें मैन्युअल रूप से कार्यान्वित गुणों पर स्वत: कार्यान्वित गुणों का उपयोग करने का कोई कारण? अद्वितीय / यादृच्छिक नामों के साथ फाइलें स्टोर करें अद्वितीय समाधान के साथ सुडोकू बोर्ड कैसे उत्पन्न करें पायथन फ़ंक्शन रिटर्निंग कोई नहीं जब मैं "रन" कमांड को निष्पादित करता हूँ तो मैं कैसे उपयोग करता है डिफ़ॉल्ट पोर्ट (9000) को बदल सकता हूँ? ग्रेड – गैर-शून्य निकास मूल्य क्या है और मैं इसे कैसे ठीक कर सकता हूं? एक संख्या (1, 2, 3) को एक स्ट्रिंग में बदलने (एक, दो, तीन) PHP में आईओएस HTTP पोस्ट का उपयोग छवि और पाठ अपलोड करें

जावास्क्रिप्ट-स्मृति विचारों में एक वायदेय श्रृंखला का निर्माण करना

इस जवाब में , एक वायदेय श्रृंखला को पुनरावृत्त रूप से बनाया गया है

थोड़ा सा सरलीकृत, हमारे पास:

function foo() { function doo() { // always return a promise if (/* more to do */) { return doSomethingAsync().then(doo); } else { return Promise.resolve(); } } return doo(); // returns a promise } 

संभवत: यह एक कॉल स्टैक और एक वादा श्रृंखला को जन्म देगा – यानी "गहरा" और "चौड़ा"।

मैं एक मेमोरी स्पाइक की अपेक्षा करता हूं जो कि केवल एक पुनरावर्ती प्रदर्शन या केवल एक वादे श्रृंखला का निर्माण करता है

  • क्या ऐसा है?
  • क्या किसी ने इस तरह से चेन बनाने के स्मृति मुद्दे पर विचार किया है?
  • क्या मेमोरी खपत वादा लिबड्स के बीच भिन्न होता है?

वेब के समाधान से एकत्रित समाधान "जावास्क्रिप्ट-स्मृति विचारों में एक वायदेय श्रृंखला का निर्माण करना"

एक कॉल स्टैक और एक वायर्ड चेन – यानी "गहरा" और "चौड़ा"

दरअसल नहीं। वहाँ कोई वादा श्रृंखला नहीं है जैसा कि हम doSomeThingAsynchronous.then(doSomethingAsynchronous).then(doSomethingAsynchronous).… से जानते हैं। doSomeThingAsynchronous.then(doSomethingAsynchronous).then(doSomethingAsynchronous).… (जो वही है जो Promise.each या doSomeThingAsynchronous.then(doSomethingAsynchronous).then(doSomethingAsynchronous).… अनुक्रमिक निष्पादित करने के लिए हो सकता है अगर यह इस तरह लिखा गया था)।

हम यहाँ क्या सामना कर रहे हैं एक संकल्प श्रृंखला है 1 – क्या अंत में होता है, जब recursion के आधार का मामला पूरा हो गया है, वही Promise.resolve(Promise.resolve(Promise.resolve(…))) जाता है। Promise.resolve(Promise.resolve(Promise.resolve(…))) । यह केवल "गहरा" है, "विस्तृत" नहीं, यदि आप इसे कॉल करना चाहते हैं

मैं एक मेमोरी स्पाइक की अपेक्षा करता हूं जो कि केवल एक पुनरावर्ती प्रदर्शन या केवल एक वादे श्रृंखला का निर्माण करता है

नहीं वास्तव में एक स्पाइक आप धीरे-धीरे, समय के साथ, बहुत सारे वादों का निर्माण करते हैं जो अंतरतम एक के साथ हल हो जाते हैं, सभी एक ही परिणाम का प्रतिनिधित्व करते हैं। जब आपके कार्य के अंत में, स्थिति पूरी हो जाती है और वास्तविक मूल्य के साथ सुलझाया गया सबसे कम वादा, इन सभी वादों को उसी मूल्य के साथ हल किया जाना चाहिए। यह संकल्प चेन को चलाने के लिए O(n) लागत से समाप्त होगा (यदि निष्कपट ढंग से लागू किया गया हो, तो यह पुनरावर्ती रूप से भी किया जा सकता है और स्टैक अतिप्रवाह का कारण बन सकता है)। उसके बाद, बाहरी सभी को छोड़कर सभी वादे एकत्रित कचरा बन सकते हैं।

इसके विपरीत, एक वायदा श्रृंखला जो कुछ के द्वारा बनाई गई है

 […].reduce(function(prev, val) { // successive execution of fn for all vals in array return prev.then(() => fn(val)); }, Promise.resolve()) 

एक स्पाइक दिखाएगा, एक ही समय में n वायर्ड ऑब्जेक्ट्स को आवंटित करेगा, और फिर धीरे-धीरे उन्हें एक-एक करके हल कर देगा, कचरा-पिछला वाले को इकट्ठा करे, जब तक कि बस समाप्त होने वाला वादा जिंदा न हो।

 memory ^ resolve promise "then" (tail) | chain chain recursion | /| |\ | / | | \ | / | | \ | ___/ |___ ___| \___ ___________ | +----------------------------------------------> time 

क्या ऐसा है?

जरुरी नहीं। जैसा कि ऊपर कहा गया है, उस थोक में सभी वादे अंत में उसी मूल्य 2 के साथ हल किए गए हैं, इसलिए सभी को हमें एक बार में सबसे बाहरी और अंतःस्थापित वादे को स्टोर करने की आवश्यकता होगी। सभी मध्यवर्ती वादे जितनी जल्दी हो सके कचरा एकत्र हो सकते हैं, और हम निरंतर अंतरिक्ष और समय में इस पुनरावर्ती चलाने के लिए चाहते हैं।

वास्तव में, यह पुनरावर्ती निर्माण एक गतिशील स्थिति (कोई निश्चित संख्या में कदम नहीं) के साथ असिंक्रोनस लूप के लिए पूरी तरह से आवश्यक है, आप वास्तव में इसे से बच नहीं सकते। हास्केल में, जहां IO मोनद के लिए हर समय उपयोग किया जाता है, इसके लिए अनुकूलन सिर्फ इस मामले के कारण लागू किया गया है। यह पूंछ कॉल पुनरावृत्ति के समान है, जो नियमित रूप से कंपलर द्वारा समाप्त कर दिया जाता है।

क्या किसी ने इस तरह से चेन बनाने के स्मृति मुद्दे पर विचार किया है?

हाँ। इस पर वादों पर चर्चा हुई थी – उदाहरण के लिए, हालांकि अभी तक कोई परिणाम नहीं है।

कई वादे वाली लाइब्रेरी वायदा की मदद से मददगारों का समर्थन करती हैं ताकि वादा की जड़ से बचें, ब्लूबर्ड के each और map तरीके जैसे चेन।

मेरी अपनी वादा लाइब्रेरी 3,4 मेमोरी या रनटाइम ओवरहेड को शुरू किए बिना हल करने वाली चेन को सुविधा देती है। जब एक वादा दूसरे को अपनाता है (भले ही अभी भी लंबित हो), तो वे अलग-अलग नहीं हो जाते, और मध्यवर्ती वादों को अब कहीं भी संदर्भित नहीं किया जाता है।

क्या मेमोरी खपत वादा लिबड्स के बीच भिन्न होता है?

हाँ। हालांकि इस मामले को अनुकूलित किया जा सकता है, यह शायद ही कभी है। विशेष रूप से, ES6 युक्ति को प्रत्येक resolve कॉल पर मूल्य का निरीक्षण करने के लिए वादा करता है, इसलिए श्रृंखला को तोड़ना संभव नहीं है। श्रृंखला में वादों को अलग-अलग मूल्यों के साथ भी हल किया जा सकता है (वास्तविक उदाहरण में नहीं, बल्कि एक उदाहरण वस्तु का निर्माण करके, जो दुर्व्यवहार प्राप्त करता है)। इस मुद्दे को एस्डिस्क पर उठाया गया था लेकिन अनसुलझे रहता है।

इसलिए यदि आप एक लीक क्रियान्वयन का उपयोग करते हैं, लेकिन अतुल्यकालिक पुनरावर्तन की आवश्यकता होती है, तो आप बेहतर कॉलबैक पर वापस स्विच करते हैं और एक ही नतीजे के वादे के परिणामस्वरूप अंतराल के वादे के फैलाव के लिए स्थगित प्रतिकृति का उपयोग करते हैं

[1]: कोई आधिकारिक शब्दावली नहीं है
[2]: ठीक है, वे एक दूसरे के साथ हल कर रहे हैं लेकिन हम उन्हें उसी मूल्य के साथ हल करना चाहते हैं , हम उम्मीद करते हैं कि
[3]: undocumented खेल का मैदान, एपल्स पास करता है अपने जोखिम पर कोड पढ़ें: https://github.com/bergus/F-Promise
[4]: इस पुल अनुरोध में पंथ के लिए भी कार्यान्वित किया गया है

अस्वीकरण: समय से पहले अनुकूलन खराब है, प्रदर्शन अंतर के बारे में पता लगाने का वास्तविक तरीका आपके कोड को बेंचमार्क करना है , और आपको इस बारे में चिंता नहीं करनी चाहिए (मुझे केवल एक बार करना पड़ा है और मैंने कम से कम 100 परियोजनाओं के लिए वादे किए हैं) ।

क्या ऐसा है?

हाँ , ये वादा करने के लिए "याद रखना" होगा कि वे क्या अनुसरण कर रहे हैं, यदि आप ऐसा 10000 वादे के लिए करते हैं तो आपके पास एक 10000 लंबी वादा श्रृंखला होगी, यदि आप नहीं तो आप नहीं करेंगे (उदाहरण के लिए, पुनरावर्ती के साथ) – किसी भी कतार प्रवाह नियंत्रण के लिए यह सच है

अगर आपको 10000 अतिरिक्त चीजें (ऑपरेशन) का ट्रैक रखना है, तो आपको इसके लिए स्मृति रखने की जरूरत है और इसमें समय लगता है, अगर वह नंबर दस लाख होता है तो यह व्यवहार्य नहीं होगा यह पुस्तकालयों के बीच बदलता रहता है।

क्या किसी ने इस तरह से चेन बनाने के स्मृति मुद्दे पर विचार किया है?

बेशक, यह एक बड़ा मुद्दा है, और एक तरह से कुछ का उपयोग करने का मामला है जो Promise.each है।

मैं एक व्यक्तिगत रूप से अपने कोड में एक त्वरित ऐप के लिए इस शैली से बचने के लिए हूं, जो एक बार वीएम में सभी फाइलों को पार करता है – लेकिन अधिकांश मामलों में यह एक गैर मुद्दा है।

क्या मेमोरी खपत वादा लिबड्स के बीच भिन्न होता है?

हां, बहुत से उदाहरण के लिए ब्लूबर्ड 3.0 एक अतिरिक्त कतार का आवंटन नहीं करेगा, यदि यह पता लगाता है कि वायर्ड ऑपरेशन पहले से ही एसिंक्रोनस (उदाहरण के लिए अगर यह प्रॉमीस। के साथ शुरू होता है) और सिंक्रोनस तरीके से निष्पादित करेगा (क्योंकि एसिंक गारंटीएं पहले ही संरक्षित हैं)।

इसका मतलब यह है कि जो मैंने पहले प्रश्न के जवाब में दावा किया था वह हमेशा सही नहीं होता (लेकिन नियमित उपयोग के मामले में यह सच है) 🙂 जब तक आंतरिक सहायता प्रदान नहीं की जाती है तब तक मूल वादे कभी ऐसा नहीं कर पाएंगे।

फिर से – यह कोई आश्चर्य नहीं है क्योंकि वादा पुस्तकालय एक दूसरे से परिमाण के आदेशों से भिन्न होते हैं।

भयानक मौजूदा उत्तरों को पूरक करने के लिए मैं अभिव्यक्ति को वर्णन करना चाहूंगा, जो कि ऐसे अतुल्यकालिक पुनरावर्ती का परिणाम है। सादगी के लिए मैं एक साधारण फ़ंक्शन का उपयोग करता हूं जो किसी दिए गए आधार और प्रतिपादक की शक्ति का मेल करता है रिकर्सिव और बेस केस ओपी के उदाहरण के बराबर हैं:

 const powerp = (base, exp) => exp === 0 ? Promise.resolve(1) : new Promise(res => setTimeout(res, 0, exp)).then( exp => power(base, exp - 1).then(x => x * base) ); powerp(2, 8); // Promise {...[[PromiseValue]]: 256} 

कुछ प्रतिस्थापन की मदद से पुनरावर्ती भाग को बदला जा सकता है। कृपया ध्यान दें कि इस अभिव्यक्ति का मूल्यांकन आपके ब्राउज़र में किया जा सकता है:

 // apply powerp with 2 and 8 and substitute the recursive case: 8 === 0 ? Promise.resolve(1) : new Promise(res => setTimeout(res, 0, 8)).then( res => 7 === 0 ? Promise.resolve(1) : new Promise(res => setTimeout(res, 0, 7)).then( res => 6 === 0 ? Promise.resolve(1) : new Promise(res => setTimeout(res, 0, 6)).then( res => 5 === 0 ? Promise.resolve(1) : new Promise(res => setTimeout(res, 0, 5)).then( res => 4 === 0 ? Promise.resolve(1) : new Promise(res => setTimeout(res, 0, 4)).then( res => 3 === 0 ? Promise.resolve(1) : new Promise(res => setTimeout(res, 0, 3)).then( res => 2 === 0 ? Promise.resolve(1) : new Promise(res => setTimeout(res, 0, 2)).then( res => 1 === 0 ? Promise.resolve(1) : new Promise(res => setTimeout(res, 0, 1)).then( res => Promise.resolve(1) ).then(x => x * 2) ).then(x => x * 2) ).then(x => x * 2) ).then(x => x * 2) ).then(x => x * 2) ).then(x => x * 2) ).then(x => x * 2) ).then(x => x * 2); // Promise {...[[PromiseValue]]: 256} 

व्याख्या:

  1. new Promise(res => setTimeout(res, 0, 8)) के साथ निष्पादक को तत्काल लागू किया जाता है और एक गैर ब्लॉकलिंग कंप्यूटेशन निष्पादित करता है ( setTimeout साथ नकल)। फिर एक अस्थिर Promise वापस आ गया है यह ओपी के उदाहरण के doSomethingAsync() के बराबर है।
  2. एक संकल्प कॉलबैक इस Promise साथ जुड़ा हुआ है। तब .then(... नोट: इस कॉलबैक का शरीर powerp के शरीर के साथ प्रतिस्थापित किया गया था।
  3. प्वाइंट 2) दोहराया जाता है और एक नेस्टेड then हेन्डलर संरचना का निर्माण तब तक होता है जब तक कि रिकर्सन के बेस केस तक नहीं पहुंच जाते। आधार मामला 1 साथ हल किया Promise देता है
  4. नेस्टेड then हेन्डलर संरचना संबंधित कॉलबैक को कॉल करके तदनुसार "अनजान" है।

जनित संरचना नेस्टेड और जंजीर क्यों नहीं है? चूंकि हेन्डलर के भीतर पुनरावर्ती मामले उन्हें मूल मान तक पहुंचने से रोकता है, जब तक कि बेस केस तक नहीं पहुंच जाता है।

यह स्टैक के बिना कैसे काम कर सकता है? संबंधित कॉलबैक एक "श्रृंखला" बनाते हैं, जो मुख्य ईवेंट लूप के लगातार माइक्रोटस्क को पुल करता है।

मैं सिर्फ एक हैक से बाहर आया जो समस्या को सुलझाने में मदद कर सकता है: पिछले में पुनरावर्ती न करें, बल्कि इसे अंतिम catch , क्योंकि catch को हल करने वाली श्रृंखला से बाहर है। अपने उदाहरण का प्रयोग करना, यह ऐसा होगा:

 function foo() { function doo() { // always return a promise if (/* more to do */) { return doSomethingAsync().then(function(){ throw "next"; }).catch(function(err) { if (err == "next") doo(); }) } else { return Promise.resolve(); } } return doo(); // returns a promise } 

यह वादा पैटर्न एक पुनरावर्ती श्रृंखला उत्पन्न करेगा। इसलिए, प्रत्येक संकल्प () कुछ मेमोरी का उपयोग करते हुए एक नया स्टैक फ्रेम (अपने डेटा के साथ) बनायेगा इसका अर्थ यह है कि इस वादा पैटर्न का उपयोग करने वाली बड़ी संख्या में चेन फ़ंक्शंस स्टैक अतिप्रवाह त्रुटियों का उत्पादन कर सकते हैं।

इसे समझाने के लिए, मैं एक लघु वरीयता पुस्तकालय का उपयोग करूँगा जिसका नाम सीक्वेंस है , जिसे मैंने लिखा है। यह चेन कार्यों के लिए अनुक्रमिक निष्पादन प्राप्त करने के लिए पुनरावर्ती पर निर्भर करता है:

 var funcA = function() { setTimeout(function() {console.log("funcA")}, 2000); }; var funcB = function() { setTimeout(function() {console.log("funcB")}, 1000); }; sequence().chain(funcA).chain(funcB).execute(); 

अनुक्रम 0-500 फ़ंक्शंस की सीमा में छोटे / मध्यम आकार की चेन के लिए महान काम करता है। हालांकि, लगभग 600 श्रृंखला में अनुक्रम अवक्रमित होता है और अक्सर अतिवृद्धि त्रुटियों को उत्पन्न करता है।

निचला रेखा है: वर्तमान में , पुनरावर्ती-आधारित वायदा पुस्तकालय छोटे / मध्यम आकार की फ़लक श्रृंखलाओं के लिए अधिक उपयुक्त होते हैं, जबकि कम-आधारित वादा लागू करने के लिए सभी मामलों के लिए ठीक है, जिसमें बड़ी श्रृंखला भी शामिल है।

बेशक इसका मतलब यह नहीं है कि पुनरावर्तन-आधारित वादे खराब हैं। हमें उनके मन में अपनी सीमाओं के साथ उपयोग करने की आवश्यकता है इसके अलावा, यह दुर्लभ है कि आपको चेन की आवश्यकता होगी कि वादे के माध्यम से कई कॉल (> = 500) मैं आमतौर पर उन्हें एसिंक कॉन्फ़िगरेशन के लिए उपयोग कर रहा हूं जो कि भारी अजाक्स का उपयोग करता है लेकिन भले ही सबसे जटिल मामलों में मैंने 15 से अधिक श्रृंखलाओं के साथ कोई स्थिति नहीं देखी है

एक और बात…

ये आंकड़े मेरी लाइब्रेरी – प्रेजिनर – के साथ किए गए परीक्षणों से प्राप्त किए गए – जो कि किसी निर्दिष्ट अंतराल के भीतर फ़ंक्शन कॉल की हासिल संख्या को प्राप्त करता है।