स्पष्ट वादा निर्माण एंटीपैटर्न क्या है और मैं इसे कैसे बचा सकता हूं?


516

मैं कोड लिख रहा था जो कुछ ऐसा दिखता है जैसे:

function getStuffDone(param) {           | function getStuffDone(param) {
    var d = Q.defer(); /* or $q.defer */ |     return new Promise(function(resolve, reject) {
    // or = new $.Deferred() etc.        |     // using a promise constructor
    myPromiseFn(param+1)                 |         myPromiseFn(param+1)
    .then(function(val) { /* or .done */ |         .then(function(val) {
        d.resolve(val);                  |             resolve(val);
    }).catch(function(err) { /* .fail */ |         }).catch(function(err) {
        d.reject(err);                   |             reject(err);
    });                                  |         });
    return d.promise; /* or promise() */ |     });
}                                        | }

किसी ने मुझे बताया कि इसे क्रमशः " आस्थगित एंटीपैटर्न " या " Promiseकंस्ट्रक्टर एंटीपैटर्न " कहा जाता है, इस कोड के बारे में क्या बुरा है और इसे एंटीपैटर्न क्यों कहा जाता है ?


क्या मैं इस बात की पुष्टि कर सकता हूं कि इससे दूर ले जाना (दाएं, बाएं नहीं, उदाहरण के संदर्भ में) getStuffDoneफ़ंक्शन रैपर को हटा दें और बस प्रोमिस शाब्दिक का उपयोग करें?
डेमबिंस्की

1
या रैपर catchमें ब्लॉक getStuffDoneएंटीपर्टन है?
डेंबिंस्की

1
देशी के लिए कम से कम Promiseउदाहरण आप भी के लिए अनावश्यक समारोह रैपर है .thenऔर .catchसंचालकों (यह यानी सिर्फ हो सकता है .then(resolve).catch(reject)।) विरोधी पैटर्न का एक आदर्श तूफान।
नूह फ्रीटस

6
@NoahFreitas उस कोड को लिखा है जिस तरह से उपदेशात्मक उद्देश्यों के लिए। मैंने इस प्रश्न और उत्तर को ऐसे लोगों की मदद करने के लिए लिखा है, जो बहुत सारे कोड पढ़ने के बाद इस मुद्दे में भाग लेते हैं: :)
बेंजामिन ग्रुएनबाम

यह भी देखें stackoverflow.com/questions/57661537/... कैसे न केवल स्पष्ट वादा निर्माण, लेकिन एक वैश्विक चर के उपयोग के रूप में अच्छी तरह से समाप्त करने के लिए के लिए।
डेविड स्पेक्टर

जवाबों:


357

आस्थगित antipattern (अब स्पष्ट निर्माणाधीन antipattern) द्वारा गढ़ा Esailija एक आम antipattern लोग हैं, जो नए वादों बनाने के लिए कर रहे हैं, मैं इसे अपने आप जब मैं पहली बार इस्तेमाल किया वादे कर दिया है। उपरोक्त कोड के साथ समस्या यह है कि श्रृंखला का वादा करने वाले तथ्य का उपयोग करने में विफल रहता है।

वादे चेन के साथ .thenहो सकते हैं और आप सीधे वादे वापस कर सकते हैं। आपके कोड को getStuffDoneफिर से लिखा जा सकता है:

function getStuffDone(param){
    return myPromiseFn(param+1); // much nicer, right?
}

वादे सभी अतुल्यकालिक कोड को अधिक पठनीय बनाने के बारे में हैं और उस तथ्य को छिपाए बिना तुल्यकालिक कोड की तरह व्यवहार करते हैं। वादे एक समय ऑपरेशन के मूल्य पर एक अमूर्तता का प्रतिनिधित्व करते हैं, वे एक प्रोग्रामिंग भाषा में एक बयान या अभिव्यक्ति की धारणा को सार करते हैं।

जब आप API को वादों में परिवर्तित कर रहे हों, तब आप केवल आस्थगित वस्तुओं का उपयोग करें और यह स्वचालित रूप से नहीं कर सकते हैं, या जब आप एकत्रीकरण कार्य लिख रहे हैं जो इस तरह से आसान होते हैं।

कोटिंग एस्सलाइजा:

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


@BenjaminGruenbaum: मैं इसके लिए deferreds के अपने उपयोग में विश्वास करता हूं, इसलिए नए प्रश्न की कोई आवश्यकता नहीं है। मुझे लगा कि यह एक उपयोग-मामला है जो आप अपने उत्तर से गायब थे। मैं जो कर रहा हूं वह एकत्रीकरण के विपरीत की तरह लगता है, है ना?
mhelvens

1
@mhelvens यदि आप मैन्युअल रूप से एक गैर-कॉलबैक एपीआई को एक वादा एपीआई में विभाजित कर रहे हैं जो "वादों के लिए कॉलबैक एपीआई को परिवर्तित करता है" फिट बैठता है। एंटीपार्टनर एक अच्छे कारण के लिए एक और वादे में एक वादा लपेटने के बारे में है, आप एक वादे को शुरू करने के लिए नहीं लपेट रहे हैं ताकि यह यहां लागू न हो।
बेंजामिन ग्रुएनबाम

@BenjaminGruenbaum: आह, हालांकि मैं खुद को आस्थगित मान रहा था, लेकिन उन्हें नीचा दिखाने के साथ क्या किया जा रहा था, और आप "वादों के लिए एक एपीआई परिवर्तित करना" का उल्लेख कर रहे हैं (जो कि शुरू होने का वादा नहीं करने का भी मामला है)।
mhelvens 20

@mhelvens मुझे लगता है कि अतिरिक्त आस्थगित विरोधी पैटर्न वास्तव में जो करता है उसके लिए अधिक सटीक होगा। ब्लूबर्ड ने .defer()एपीआई को नए (और सुरक्षित रूप से फेंकने) वादा निर्माणकर्ता में पदावनत किया, यह (किसी भी तरह से) निर्माण वादों की धारणा को
चित्रित नहीं

1
धन्यवाद @ Roamer-1888 आपके संदर्भ से मुझे पता चला कि मेरा मुद्दा क्या था। ऐसा लगता है कि मैं इसे महसूस किए बिना नेस्टेड (बिना अनुमति के) वादे कर रहा था।
घुइरो

134

इसके साथ गलत क्या है?

लेकिन पैटर्न काम करता है!

तुम भाग्यशाली हो। दुर्भाग्य से, यह संभवतः नहीं है, जैसा कि आप की संभावना है कि कुछ किनारे के मामले को भूल गए। मैंने देखी गई घटनाओं में से आधे से अधिक में, लेखक त्रुटि हैंडलर की देखभाल करना भूल गया है:

return new Promise(function(resolve) {
    getOtherPromise().then(function(result) {
        resolve(result.property.example);
    });
})

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

मामले में ऐसा ही होता है कि आपका कॉलबैक कोड एक त्रुटि का कारण बनता है - जैसे कि जब कोई अपवाद resultनहीं होता है propertyऔर एक अपवाद फेंक दिया जाता है। यह बिना सोचे-समझे चल जाएगा और नए वादे को अनसुलझा छोड़ देगा।

इसके विपरीत, का उपयोग करते हुए .then()स्वचालित रूप से इन दोनों परिदृश्यों का ध्यान रखा जाता है, और त्रुटि होने पर नए वादे को अस्वीकार करता है:

 return getOtherPromise().then(function(result) {
     return result.property.example;
 })

आस्थगित एंटीपैटर्न न केवल बोझिल है, बल्कि त्रुटि-प्रवण भी है । .then()चेनिंग के लिए उपयोग करना अधिक सुरक्षित है।

लेकिन मैंने सब कुछ संभाला है!

वास्तव में? अच्छा। हालांकि, यह बहुत विस्तृत और प्रचुर होगा, खासकर यदि आप एक वादा पुस्तकालय का उपयोग करते हैं जो रद्द करने या संदेश पास करने जैसी अन्य सुविधाओं का समर्थन करता है। या शायद यह भविष्य में होगा, या आप अपने पुस्तकालय को एक बेहतर के खिलाफ स्वैप करना चाहते हैं? आप उसके लिए अपने कोड को फिर से लिखना नहीं चाहेंगे।

पुस्तकालयों के तरीके ( then) केवल सभी सुविधाओं का मूल रूप से समर्थन नहीं करते हैं, उनके स्थान पर कुछ अनुकूलन भी हो सकते हैं। उनका उपयोग करने से संभवतः आपके कोड में तेज़ी आएगी, या कम से कम लाइब्रेरी के भविष्य के संशोधनों द्वारा अनुकूलित होने की अनुमति होगी।

मैं इससे कैसे बचूं?

इसलिए जब भी आप खुद को मैन्युअल रूप से एक Promiseया Deferredपहले से मौजूद वादों को शामिल करते हुए पाते हैं, तो पहले पुस्तकालय एपीआई की जांच करें । डिफर्ड एंटीपैटर्न अक्सर उन लोगों द्वारा लागू किया जाता है जो वादे करते हैं [केवल] एक पर्यवेक्षक पैटर्न के रूप में देखते हैं - लेकिन वादे कॉलबैक से अधिक हैं : उन्हें रचना योग्य माना जाता है। प्रत्येक सभ्य पुस्तकालय में हर विचारशील तरीके से वादों की रचना के लिए बहुत आसान उपयोग कार्य हैं, उन सभी निम्न-स्तरीय सामानों की देखभाल करना, जिनसे आप निपटना नहीं चाहते हैं।

यदि आपको एक नए तरीके से कुछ वादों की रचना करने की आवश्यकता है जो एक मौजूदा सहायक फ़ंक्शन द्वारा समर्थित नहीं है, तो अपरिहार्य डिफ्रेड्स के साथ अपना फ़ंक्शन लिखना आपका अंतिम विकल्प होना चाहिए। एक और अधिक उपयोगी पुस्तकालय में जाने पर विचार करें, और / या अपने वर्तमान पुस्तकालय के खिलाफ बग दर्ज करें। इसके अनुरक्षक को मौजूदा कार्यों से रचना प्राप्त करने में सक्षम होना चाहिए, आपके लिए एक नया सहायक फ़ंक्शन लागू करना चाहिए और / या उन किनारे मामलों की पहचान करने में मदद करना चाहिए जिन्हें संभालने की आवश्यकता है।


क्या ऐसे उदाहरण हैं, जिनमें फ़ंक्शन के अलावा setTimeout, जहां कंस्ट्रक्टर का उपयोग किया जा सकता है, लेकिन "प्रोमिस कंस्ट्रक्टर एनीपैटर्न" नहीं माना जा सकता है?
271314

1
@ guest271314: सब कुछ अतुल्यकालिक है कि एक वादा वापस नहीं करता है। हालांकि अक्सर पर्याप्त आप पुस्तकालयों के समर्पित प्रॉमिसिफिकेशन हेल्पर्स के साथ बेहतर परिणाम प्राप्त करते हैं। और सुनिश्चित करें कि हमेशा सबसे निचले स्तर पर प्रॉमिस करें, इसलिए यह " फंक्शन सहितsetTimeout " नहीं है, बल्कि " फंक्शन setTimeoutही " है।
बेर्गी

"और सुनिश्चित करें कि हमेशा सबसे निचले स्तर पर प्रॉमिस करें, इसलिए यह" सहित एक फंक्शन setTimeout"नहीं है, लेकिन" फंक्शन setTimeoutही "" दोनों के बीच अंतर का वर्णन कर सकता है?
.१३१४

@ guest271314 एक फ़ंक्शन जिसमें केवल कॉल शामिल है , फ़ंक्शनsetTimeout से स्पष्ट रूप से भिन्न है , है ना? setTimeout
बरगी

4
मुझे लगता है कि यहां एक महत्वपूर्ण सबक, एक जिसे अभी तक स्पष्ट रूप से नहीं बताया गया है, वह यह है कि एक वादा और इसकी जंजीर 'फिर' एक अतुल्यकालिक ऑपरेशन का प्रतिनिधित्व करता है: प्रारंभिक ऑपरेशन वादा निर्माता में है और अंतिम समापन बिंदु 'में है फिर 'समारोह। इसलिए यदि आपके पास एक एसिंक्र ऑपरेशन के बाद एक सिंक ऑपरेशन है, तो सिंक सामान को प्रॉमिस में डालें। यदि आपके पास सिंक के बाद एक async ऑपरेशन है, तो सिंक सामान को 'तब' में रखें। पहले मामले में, मूल वादा वापस करें। दूसरे मामले में, वादा / फिर श्रृंखला (जो एक वादा भी है) लौटाएं।
डेविड स्पेक्टर
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.