मैं .then () श्रृंखला में पिछले वादा परिणामों तक कैसे पहुँच सकता हूँ?


650

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

function getExample() {
    return promiseA(…).then(function(resultA) {
        // Some processing
        return promiseB(…);
    }).then(function(resultB) {
        // More processing
        return // How do I gain access to resultA here?
    });
}

2
यह प्रश्न वास्तव में दिलचस्प है और भले ही इसे टैग किया गया हो javascript, यह अन्य भाषा में प्रासंगिक है। मैं सिर्फ जावा और jdeferred में "चेन को तोड़ने" के जवाब का उपयोग करता हूं
gontard

जवाबों:


377

कड़ी तोड़ें

जब आपको अपनी श्रृंखला में मध्यवर्ती मूल्यों तक पहुंचने की आवश्यकता होती है, तो आपको अपनी श्रृंखला को उन एकल टुकड़ों में विभाजित करना चाहिए, जिनकी आपको आवश्यकता है। एक कॉलबैक को संलग्न करने और किसी तरह इसके पैरामीटर को कई बार उपयोग करने की कोशिश करने के बजाय, एक ही वादे के लिए कई कॉलबैक संलग्न करें - जहां भी आपको परिणाम मूल्य की आवश्यकता होती है। मत भूलो, एक वादा सिर्फ भविष्य के मूल्य का प्रतिनिधित्व करता है (परदे के पीछे) ! रैखिक श्रृंखला में एक से दूसरे वादे को प्राप्त करने के लिए, परिणाम के मूल्य का निर्माण करने के लिए अपने पुस्तकालय द्वारा आपको दिए गए वादे का उपयोग करें।

यह बहुत सीधा नियंत्रण प्रवाह, कार्यात्मकताओं की स्पष्ट संरचना और इसलिए आसान मॉड्युलराइजेशन का परिणाम देगा।

function getExample() {
    var a = promiseA(…);
    var b = a.then(function(resultA) {
        // some processing
        return promiseB(…);
    });
    return Promise.all([a, b]).then(function([resultA, resultB]) {
        // more processing
        return // something using both resultA and resultB
    });
}

इसके बाद कॉलबैक में नष्ट होने वाले पैरामीटर के बजाय Promise.allकेवल ES6 के साथ उपलब्ध होने के बाद , ES5 में thenकॉल को एक निफ्टी हेल्पर विधि द्वारा प्रतिस्थापित किया जाएगा जो कई वाद पुस्तकालयों ( क्यू , ब्लूबर्ड , कब ,…) द्वारा प्रदान किया गया था .spread(function(resultA, resultB) { …:।

ब्लूबर्ड ने उस + संयोजन को एक सरल (और अधिक कुशल) निर्माण के साथ बदलने के लिए एक समर्पित joinफ़ंक्शन भी दिया है :Promise.allspread


return Promise.join(a, b, function(resultA, resultB) {  });

1
क्या सरणी के अंदर कार्य क्रम में निष्पादित हैं?
डरावना

6
@scaryguy: सरणी में कोई कार्य नहीं हैं, वे वादे हैं। promiseAऔर promiseB(वादा-वापसी) कार्य यहाँ हैं।
बरगी

2
@ रोलैंड ने कभी नहीं कहा कि यह :-) यह जवाब ES5 उम्र में लिखा गया था, जहां कोई भी वादा मानक में नहीं spreadथा , और इस पैटर्न में सुपर उपयोगी था। अधिक आधुनिक समाधानों के लिए स्वीकृत उत्तर देखें। हालाँकि, मैंने पहले से ही स्पष्ट-स्पष्ट उत्तर को अपडेट कर दिया है , और वास्तव में इस एक को भी अपडेट नहीं करने का कोई अच्छा कारण नहीं है।
बेर्गी

1
@ नहीं, आप ऐसा नहीं करना चाहिए , यह अस्वीकार के साथ परेशानी लाएगा।
बरगी

1
मैं इस उदाहरण को नहीं समझता। यदि 'फिर' कथनों की एक श्रृंखला है, जिसके लिए आवश्यक है कि मानों को पूरे श्रृंखला में प्रचारित किया जाए, तो मैं यह नहीं देखता कि यह समस्या कैसे हल करता है। एक वादा जिसके लिए पिछले मूल्य की आवश्यकता हो, जब तक कि मूल्य मौजूद न हो, उसे निकाल दिया (बनाया) जा सकता है। इसके अलावा, Promise.all () बस अपनी सूची में सभी वादे खत्म होने का इंतजार करता है: यह एक आदेश लागू नहीं करता है। इसलिए मुझे सभी पिछले मानों तक पहुंच के लिए प्रत्येक 'अगले' फ़ंक्शन की आवश्यकता है और मैं नहीं देखता कि आपका उदाहरण कैसे होता है। आपको अपने उदाहरण के माध्यम से हमें चलना चाहिए, क्योंकि मैं इस पर विश्वास नहीं करता या इसे नहीं समझता।
डेविड स्पेक्टर

238

ECMAScript सद्भाव

बेशक, इस समस्या को भाषा डिजाइनरों द्वारा भी मान्यता दी गई थी। उन्होंने बहुत काम किया और async फ़ंक्शन प्रस्ताव ने आखिरकार इसे बना दिया

ECMAScript 8

आपको एक भी thenआह्वान या कॉलबैक फ़ंक्शन की आवश्यकता नहीं है , जैसा कि एक अतुल्यकालिक फ़ंक्शन (जो कि जब बुलाया जा रहा है एक वादा लौटाता है) तो आप सीधे वादों के लिए इंतजार कर सकते हैं। इसमें स्थितियों, लूप्स और ट्राई-कैच-क्लॉस जैसी मनमानी नियंत्रण संरचनाएं भी शामिल हैं, लेकिन सुविधा के लिए हमें यहां की आवश्यकता नहीं है:

async function getExample() {
    var resultA = await promiseA(…);
    // some processing
    var resultB = await promiseB(…);
    // more processing
    return // something using both resultA and resultB
}

ECMAScript 6

जब हम ईएस 8 की प्रतीक्षा कर रहे थे, तो हमने पहले से ही बहुत ही समान प्रकार के सिंटैक्स का उपयोग किया था। ईएस 6 जनरेटर कार्यों के साथ आया था , जो मनमाने ढंग से रखे गए yieldखोजशब्दों पर टुकड़ों में निष्पादन को तोड़ने की अनुमति देता है । उन स्लाइसों को एक दूसरे के बाद, स्वतंत्र रूप से, यहां तक ​​कि अतुल्यकालिक रूप से चलाया जा सकता है - और यही वह है जो हम तब करते हैं जब हम अगले कदम को चलाने से पहले एक वादा संकल्प के लिए इंतजार करना चाहते हैं।

समर्पित पुस्तकालय (जैसे co या task.js ) हैं, लेकिन कई वादे पुस्तकालयों में सहायक कार्य ( Q , Bluebird , कब ,…) हैं, जो आपके लिए एक जनरेटर फ़ंक्शन देने पर आपके लिए यह async चरण-दर-चरण निष्पादन करते हैं पैदावार का वादा किया।

var getExample = Promise.coroutine(function* () {
//               ^^^^^^^^^^^^^^^^^ Bluebird syntax
    var resultA = yield promiseA(…);
    // some processing
    var resultB = yield promiseB(…);
    // more processing
    return // something using both resultA and resultB
});

यह संस्करण 4.0 के बाद से Node.js में काम किया, कुछ ब्राउज़रों (या उनके देव संस्करणों) ने अपेक्षाकृत जल्दी ही जनरेटर सिंटैक्स का समर्थन किया।

ECMAScript 5

हालाँकि, यदि आप चाहते हैं / पीछे की ओर संगत होने की आवश्यकता है तो आप बिना ट्रांसपॉयलर के उपयोग नहीं कर सकते। जेनरेटर फ़ंक्शंस और एसिंक्स दोनों फ़ंक्शंस वर्तमान टूलींग द्वारा समर्थित हैं, उदाहरण के लिए जनरेटर और एसिंक्स फ़ंक्शंस पर बैबेल का प्रलेखन देखें ।

और फिर, कई अन्य संकलन-से-जेएस भाषाएं भी हैं जो अतुल्यकालिक प्रोग्रामिंग को आसान बनाने के लिए समर्पित हैं। वे आम तौर पर एक वाक्य रचना के लिए इसी तरह का उपयोग await, (उदाहरण के लिए आइस्ड CoffeeScript ), लेकिन वहाँ भी दूसरों है कि एक हास्केल की तरह सुविधा हैं do-notation (जैसे LatteJs , monadic , PureScript या LispyScript )।


@Bergi क्या आपको बाहर के कोड से async फ़ंक्शन एग्जामिनेशन getExample () की प्रतीक्षा करने की आवश्यकता है?
17

@arisalexis: हाँ, getExampleअभी भी एक फ़ंक्शन है जो एक वादा लौटाता है, अन्य उत्तरों में कार्यों की तरह ही काम करता है, लेकिन अच्छे वाक्यविन्यास के साथ। आप awaitकिसी अन्य asyncफ़ंक्शन में कॉल कर सकते हैं, या आप .then()इसके परिणाम के लिए श्रृंखला बना सकते हैं ।
Bergi

1
मैं उत्सुक हूं, आपने इसे पूछने के तुरंत बाद अपने सवाल का जवाब क्यों दिया? यहां कुछ अच्छी चर्चा है, लेकिन मैं उत्सुक हूं। हो सकता है कि पूछने के बाद आपको अपने जवाब मिलें?
ग्रैनमो

@granmoe: मैं एक विहित डुप्लिकेट लक्ष्य के रूप में उद्देश्य पर पूरी चर्चा पोस्ट
Bergi

क्या Promise.coroutine (यानी, Bluebird या किसी अन्य लाइब्रेरी का उपयोग नहीं करने के लिए, लेकिन केवल सादे JS का उपयोग करने से बचने के लिए एक बहुत ही श्रमसाध्य) तरीका नहीं है जनरेटर फ़ंक्शन के साथ ECMAScript 6 उदाहरण में? मेरे मन में कुछ था, steps.next().value.then(steps.next)...लेकिन यह काम नहीं किया।
एक शिक्षार्थी का नाम 13

102

समकालिक निरीक्षण

चर के लिए वादों के लिए बाद में जरूरत-मूल्यों को असाइन करना और फिर तुल्यकालिक निरीक्षण के माध्यम से उनका मूल्य प्राप्त करना। उदाहरण ब्लूबर्ड .value()विधि का उपयोग करता है लेकिन कई पुस्तकालय समान विधि प्रदान करते हैं।

function getExample() {
    var a = promiseA(…);

    return a.then(function() {
        // some processing
        return promiseB(…);
    }).then(function(resultB) {
        // a is guaranteed to be fulfilled here so we can just retrieve its
        // value synchronously
        var aValue = a.value();
    });
}

इसका उपयोग आप जितने मानों के लिए कर सकते हैं:

function getExample() {
    var a = promiseA(…);

    var b = a.then(function() {
        return promiseB(…)
    });

    var c = b.then(function() {
        return promiseC(…);
    });

    var d = c.then(function() {
        return promiseD(…);
    });

    return d.then(function() {
        return a.value() + b.value() + c.value() + d.value();
    });
}

6
यह मेरा पसंदीदा उत्तर है: पुस्तकालय या भाषा सुविधाओं पर पठनीय, एक्स्टेंसिबल और न्यूनतम निर्भरता
जेसन

13
@ जेसन: उह, " पुस्तकालय सुविधाओं पर न्यूनतम निर्भरता "? सिंक्रोनस निरीक्षण एक पुस्तकालय की सुविधा है, और बूट करने के लिए काफी गैर-मानक है।
बर्गी

2
मुझे लगता है कि वह पुस्तकालय विशिष्ट विशेषताओं का मतलब था
मृत्यु

54

घोंसला बनाना (और) बंद करना

चरों की गुंजाइश बनाए रखने के लिए क्लोजर का उपयोग करना (हमारे मामले में, सफलता कॉलबैक फ़ंक्शन पैरामीटर) प्राकृतिक जावास्क्रिप्ट समाधान है। वादों के साथ, हम मनमाने ढंग से घोंसला बना सकते हैं और.then() कॉलबैक को समतल कर सकते हैं - वे आंतरिक रूप से एक को छोड़कर, शब्दार्थ के बराबर हैं।

function getExample() {
    return promiseA(…).then(function(resultA) {
        // some processing
        return promiseB(…).then(function(resultB) {
            // more processing
            return // something using both resultA and resultB;
        });
    });
}

बेशक, यह एक इंडेंटेशन पिरामिड का निर्माण कर रहा है। यदि इंडेंटेशन बहुत बड़ा हो रहा है, तो आप अभी भी कयामत के पिरामिड का मुकाबला करने के लिए पुराने टूल को लागू कर सकते हैं : संशोधित करें, अतिरिक्त नामित कार्यों का उपयोग करें, और जैसे ही आपको एक चर की आवश्यकता नहीं है, वैसे ही वादा श्रृंखला को समतल करें।
सिद्धांत रूप में, आप हमेशा दो स्तरों से अधिक घोंसले के शिकार (सभी क्लोजर को स्पष्ट करके) से बच सकते हैं, व्यवहार में उपयोग के रूप में कई उचित हैं।

function getExample() {
    // preprocessing
    return promiseA(…).then(makeAhandler(…));
}
function makeAhandler(…)
    return function(resultA) {
        // some processing
        return promiseB(…).then(makeBhandler(resultA, …));
    };
}
function makeBhandler(resultA, …) {
    return function(resultB) {
        // more processing
        return // anything that uses the variables in scope
    };
}

तुम भी इस तरह का के लिए सहायक कार्यों का उपयोग कर सकते हैं आंशिक आवेदन की तरह, _.partialसे अंडरस्कोर / lodash या देशी .bind()विधि , आगे कमी खरोज रहे हैं:

function getExample() {
    // preprocessing
    return promiseA(…).then(handlerA);
}
function handlerA(resultA) {
    // some processing
    return promiseB(…).then(handlerB.bind(null, resultA));
}
function handlerB(resultA, resultB) {
    // more processing
    return // anything that uses resultA and resultB
}

5
यह वही सुझाव दिया गया है जो वादे के आधार पर नोलन लॉसन के लेख ouch उन्नत गलती # ४ ’पर दिया गया हैpouchdb.com/2015/05/18/we-have-a-problem-with-promises.html अच्छा पढ़ा है।
रॉबर्ट

2
यह ठीक bindमोनाड्स में कार्य करता है। हास्केल को सिन्क्रेटिक शुगर (डू-नोटेशन) प्रदान करता है, ताकि वह एसिंक्स / वेट सिंटैक्स जैसा दिख सके।
ज़ेरोनोन

50

स्पष्ट पास से गुजरना

कॉलबैक के घोंसले के समान, यह तकनीक क्लोजर पर निर्भर करती है। फिर भी, श्रृंखला सपाट रहती है - केवल नवीनतम परिणाम पारित करने के बजाय, प्रत्येक चरण के लिए कुछ राज्य वस्तु पारित की जाती है। ये राज्य ऑब्जेक्ट पिछले कार्यों के परिणामों को जमा करते हैं, उन सभी मूल्यों को सौंपते हैं जिन्हें बाद में फिर से और वर्तमान कार्य के परिणाम की आवश्यकता होगी।

function getExample() {
    return promiseA(…).then(function(resultA) {
        // some processing
        return promiseB(…).then(b => [resultA, b]); // function(b) { return [resultA, b] }
    }).then(function([resultA, resultB]) {
        // more processing
        return // something using both resultA and resultB
    });
}

यहां, वह छोटा तीर b => [resultA, b]वह फ़ंक्शन है जो बंद हो जाता है resultA, और अगले चरण में दोनों परिणामों की एक सरणी पास करता है। जो एकल चरों में इसे तोड़ने के लिए पैरामीटर डिस्ट्रक्टिंग सिंटैक्स का उपयोग करता है।

ईएस 6 के साथ विनाशकारी उपलब्ध होने से पहले, एक निफ्टी हेल्पर विधि कहा जाता है जिसे .spread()कई वाद पुस्तकालयों ( क्यू , ब्लूबर्ड , कब ,…) द्वारा प्रदान किया गया था । यह कई मापदंडों के साथ एक फ़ंक्शन लेता है - प्रत्येक सरणी तत्व के लिए - जैसा कि उपयोग किया जाना है .spread(function(resultA, resultB) { …

बेशक, यहाँ आवश्यक बंद करने को आगे कुछ सहायक कार्यों द्वारा सरल किया जा सकता है, जैसे

function addTo(x) {
    // imagine complex `arguments` fiddling or anything that helps usability
    // but you get the idea with this simple one:
    return res => [x, res];
}


return promiseB(…).then(addTo(resultA));

वैकल्पिक रूप से, आप Promise.allसरणी के लिए वादे का उत्पादन करने के लिए नियोजित कर सकते हैं :

function getExample() {
    return promiseA(…).then(function(resultA) {
        // some processing
        return Promise.all([resultA, promiseB(…)]); // resultA will implicitly be wrapped
                                                    // as if passed to Promise.resolve()
    }).then(function([resultA, resultB]) {
        // more processing
        return // something using both resultA and resultB
    });
}

और आप न केवल सरणियों का उपयोग कर सकते हैं, बल्कि मनमाने ढंग से जटिल वस्तुओं का उपयोग कर सकते हैं। उदाहरण के लिए, एक अलग सहायक समारोह में _.extendया उसके साथ Object.assign:

function augment(obj, name) {
    return function (res) { var r = Object.assign({}, obj); r[name] = res; return r; };
}

function getExample() {
    return promiseA(…).then(function(resultA) {
        // some processing
        return promiseB(…).then(augment({resultA}, "resultB"));
    }).then(function(obj) {
        // more processing
        return // something using both obj.resultA and obj.resultB
    });
}

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


सबसे पहले, मैं वाक्य रचना को छोड़ते हुए नहीं लगता कि Promise.allप्रोत्साहित किया जाना चाहिए (यह होगा ES6 में काम नहीं जब destructuring यह स्थान ले लेगा और स्विचिंग एक .spreadएक करने के लिए thenलोगों को अक्सर अप्रत्याशित परिणाम देता है बढ़ाने के रूप में -। मुझे यकीन है कि तुम क्यों की जरूरत है नहीं कर रहा हूँ संवर्द्धन का उपयोग करने के लिए - वादे के प्रोटोटाइप में चीजों को जोड़ना ES6 वादों का विस्तार करने के लिए स्वीकार्य तरीका नहीं है, जिसे (वर्तमान में असमर्थित) उपवर्ग के साथ बढ़ाया जाना चाहिए।
बेंजामिन ग्रुएनबाम

@BenjaminGruenbaum: " सिंटेक्स ऑउटिंगPromise.all " से आपका क्या अभिप्राय है ? इस उत्तर में कोई भी विधि ES6 के साथ नहीं टूटेगी। spreadएक विनाशकारी के लिए स्विच thenकरना या तो मुद्दों नहीं होना चाहिए। Re .prototyp.augment: मुझे पता था कि कोई इसे नोटिस करेगा, मुझे सिर्फ संभावनाएं तलाशना पसंद है - इसे संपादित करने के लिए।
बरगी

सरणी सिंटैक्स से मेरा मतलब है कि return [x,y]; }).spread(...इसके बजाय return Promise.all([x, y]); }).spread(...जब एस 6 विनाशकारी चीनी के लिए स्वैपिंग फैलता है, तो यह नहीं बदलेगा और यह एक अजीब किनारे का मामला भी नहीं होगा जहां वादे हर चीज से अलग तरीके से रिटर्निंग एरे का इलाज करते हैं।
बेंजामिन ग्रुएनबाम

3
यह शायद सबसे अच्छा जवाब है। वादे "कार्यात्मक प्रतिक्रियाशील प्रोग्रामिंग" -लाइट हैं, और यह अक्सर नियोजित समाधान है। उदाहरण के लिए, बेकनज, के पास #combineTemplate है जो आपको एक वस्तु में परिणाम संयोजित करने की अनुमति देता है जो श्रृंखला के नीचे से गुजरती है
U Avalos

1
@CapiEtheriel का जवाब तब लिखा गया था जब ES6 उतना व्यापक नहीं था जितना आज है। हाँ, शायद उदाहरणों की अदला-बदली करने का समय है
बर्गी

35

परस्पर प्रासंगिक अवस्था

तुच्छ (लेकिन अपात्र और बल्कि त्रुटिपूर्ण) समाधान केवल उच्च-गुंजाइश वाले चरों का उपयोग करना है (जिस पर श्रृंखला में सभी कॉलबैक की पहुंच है) और जब आप उन्हें प्राप्त करते हैं, तो उनके लिए परिणाम मान लिखें:

function getExample() {
    var resultA;
    return promiseA(…).then(function(_resultA) {
        resultA = _resultA;
        // some processing
        return promiseB(…);
    }).then(function(resultB) {
        // more processing
        return // something using both resultA and resultB
    });
}

कई चर के बजाय एक (प्रारंभिक रूप से खाली) ऑब्जेक्ट का भी उपयोग किया जा सकता है, जिस पर परिणाम गतिशील रूप से बनाए गए गुणों के रूप में संग्रहीत किए जाते हैं।

इस समाधान में कई कमियां हैं:

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

ब्लूबर्ड लाइब्रेरी एक वस्तु के उपयोग को प्रोत्साहित करती है जो एक संदर्भ श्रृंखला को एक संदर्भ वस्तु को असाइन करने के लिए अपने bind()तरीके का उपयोग करते हुए पारित किया जाता है । यह अन्यथा अनुपयोगी thisकीवर्ड के माध्यम से प्रत्येक कॉलबैक फ़ंक्शन से सुलभ होगा । जबकि ऑब्जेक्ट गुण चर की तुलना में undetected typos के लिए अधिक प्रवण हैं, पैटर्न काफी चालाक है:

function getExample() {
    return promiseA(…)
    .bind({}) // Bluebird only!
    .then(function(resultA) {
        this.resultA = resultA;
        // some processing
        return promiseB(…);
    }).then(function(resultB) {
        // more processing
        return // something using both this.resultA and resultB
    }).bind(); // don't forget to unbind the object if you don't want the
               // caller to access it
}

इस दृष्टिकोण को आसानी से वादा पुस्तकालयों में आसानी से अनुकरण किया जा सकता है जो समर्थन नहीं करते हैं। (हालांकि कुछ हद तक क्रियात्मक तरीके से और एक अभिव्यक्ति में उपयोग नहीं किया जा सकता है):

function getExample() {
    var ctx = {};
    return promiseA(…)
    .then(function(resultA) {
        this.resultA = resultA;
        // some processing
        return promiseB(…);
    }.bind(ctx)).then(function(resultB) {
        // more processing
        return // something using both this.resultA and resultB
    }.bind(ctx));
}

.bind()स्मृति रिसाव को रोकने के लिए अनावश्यक है
एस्लेइजा

@Esailija: लेकिन लौटाए गए वादे में संदर्भ वस्तु का संदर्भ नहीं है अन्यथा? ठीक है, बेशक कचरा संग्रह बाद में इसे संभाल लेगा; यह "लीक" नहीं है जब तक कि वादा कभी निपटाया नहीं गया।
बरगी

हां, लेकिन वादे भी उनकी पूर्ति मूल्यों और त्रुटि कारणों के संदर्भ में होते हैं ... लेकिन कुछ भी वादे के संदर्भ में नहीं होता है इसलिए यह मायने नहीं रखता है
Esailija

4
कृपया इस उत्तर को दो में तोड़ दें क्योंकि मैंने लगभग प्रस्तावना पर मतदान किया था! मुझे लगता है कि "तुच्छ (लेकिन अपात्र और बल्कि त्रुटिपूर्ण) समाधान" सबसे साफ और सरल समाधान है, क्योंकि यह आपके स्वीकृत स्व-उत्तर की तुलना में बंद और परस्पर योग्य स्थिति पर निर्भर नहीं करता है, फिर भी सरल है। क्लोजर न तो वैश्विक हैं और न ही बुराई। इस दृष्टिकोण के खिलाफ दिए गए तर्कों का मुझे कोई मतलब नहीं है कि मुझे आधार दिया जाए। क्या मौद्रिक समस्याओं को "अद्भुत लंबी फ्लैट वादा श्रृंखला" दिया जा सकता है?
जिब

2
जैसा कि मैंने ऊपर कहा, वादे "कार्यात्मक प्रतिक्रियाशील प्रोग्रामिंग" -लाइट हैं। यह FRP
U Avalos

15

"पारस्परिक संदर्भ स्थिति" पर एक कम कठोर स्पिन

एक वादा श्रृंखला में मध्यवर्ती परिणामों को इकट्ठा करने के लिए स्थानीय रूप से स्कॉप्ड ऑब्जेक्ट का उपयोग करना आपके द्वारा लगाए गए प्रश्न के लिए एक उचित दृष्टिकोण है। निम्नलिखित स्निपेट पर विचार करें:

function getExample(){
    //locally scoped
    const results = {};
    return promiseA(paramsA).then(function(resultA){
        results.a = resultA;
        return promiseB(paramsB);
    }).then(function(resultB){
        results.b = resultB;
        return promiseC(paramsC);
    }).then(function(resultC){
        //Resolve with composite of all promises
        return Promise.resolve(results.a + results.b + resultC);
    }).catch(function(error){
        return Promise.reject(error);
    });
}
  • वैश्विक चर खराब हैं, इसलिए यह समाधान स्थानीय रूप से स्कोप किए गए चर का उपयोग करता है जो कोई नुकसान नहीं पहुंचाता है। यह केवल फ़ंक्शन के भीतर पहुंच योग्य है।
  • उत्परिवर्तनीय अवस्था बदसूरत होती है, लेकिन यह बदसूरत तरीके से राज्य को उत्परिवर्तित नहीं करती है। बदसूरत परिवर्तनशील राज्य पारंपरिक रूप से फ़ंक्शन तर्क या वैश्विक चर की स्थिति को संशोधित करने के लिए संदर्भित करता है, लेकिन यह दृष्टिकोण केवल स्थानीय रूप से स्कोप किए गए चर की स्थिति को संशोधित करता है जो कि वादे के परिणामों को एकत्र करने के एकमात्र उद्देश्य के लिए मौजूद है ... एक चर जो एक साधारण मौत मर जाएगा एक बार वादा हल हो गया।
  • इंटरमीडिएट वादों को परिणाम ऑब्जेक्ट की स्थिति तक पहुंचने से नहीं रोका जाता है, लेकिन यह कुछ डरावने परिदृश्य को पेश नहीं करता है जहां श्रृंखला में वादों में से एक दुष्ट हो जाएगा और आपके परिणामों को तोड़फोड़ करेगा। वादे के प्रत्येक चरण में मूल्यों को स्थापित करने की जिम्मेदारी इस फ़ंक्शन तक ही सीमित है और समग्र परिणाम या तो सही होगा या गलत होगा ... यह कुछ बग नहीं होगा जो उत्पादन में वर्षों बाद फसल देगा (जब तक कि आप इसे करने का इरादा न करें !)
  • यह एक दौड़ स्थिति परिदृश्य को पेश नहीं करता है जो समानांतर आह्वान से उत्पन्न होता है क्योंकि getExample फ़ंक्शन के प्रत्येक आह्वान के लिए परिणाम चर का एक नया उदाहरण बनाया जाता है।


धन्यवाद @Bergi, मुझे यह भी एहसास नहीं हुआ कि जब तक आप इसका उल्लेख नहीं करते तब तक यह एक विरोधी पैटर्न था!
Jay

यह संबंधित त्रुटि को कम करने के लिए एक अच्छा समाधान है। मैं ES5 का उपयोग कर रहा था और वादे के साथ काम करने के लिए एक और पुस्तकालय जोड़ना नहीं चाहता था।
नीलकंठ सिंह देव

8

नोड 7.4 अब सद्भाव ध्वज के साथ async / प्रतीक्षा कॉल का समर्थन करता है।

इसे इस्तेमाल करे:

async function getExample(){

  let response = await returnPromise();

  let response2 = await returnPromise2();

  console.log(response, response2)

}

getExample()

और फ़ाइल को इसके साथ चलाएं:

node --harmony-async-await getExample.js

सरल हो सकता है!


8

इस दिन, मैं भी तुम्हारे जैसे कुछ सवालों को पूरा करता हूं। अंत में, मैं quesition के साथ एक अच्छा समाधान ढूंढता हूं, यह सरल और पढ़ने के लिए अच्छा है। मैं आशा करता हूं यह आपकी मदद कर सकते हैं।

के अनुसार कैसे करने के लिए चेन जावास्क्रिप्ट-वादे

ठीक है, चलो कोड देखें:

const firstPromise = () => {
    return new Promise((resolve, reject) => {
        setTimeout(() => {
            console.log('first promise is completed');
            resolve({data: '123'});
        }, 2000);
    });
};

const secondPromise = (someStuff) => {
    return new Promise((resolve, reject) => {
        setTimeout(() => {
            console.log('second promise is completed');
            resolve({newData: `${someStuff.data} some more data`});
        }, 2000);
    });
};

const thirdPromise = (someStuff) => {
    return new Promise((resolve, reject) => {
        setTimeout(() => {
            console.log('third promise is completed');
            resolve({result: someStuff});
        }, 2000);
    });
};

firstPromise()
    .then(secondPromise)
    .then(thirdPromise)
    .then(data => {
        console.log(data);
    });

4
यह वास्तव में इस सवाल का जवाब नहीं देता है कि श्रृंखला में पिछले परिणामों का उपयोग कैसे किया जाए।
बरगी

2
हर वादे को पिछली कीमत मिल सकती है, आपका क्या मतलब है?
yzfdjzwl

1
प्रश्न में कोड पर एक नज़र डालें। उद्देश्य उस वादे का परिणाम प्राप्त करना नहीं .thenहै जिसे कहा जाता है, बल्कि उससे पहले के परिणाम। जैसे thirdPromiseके परिणाम तक पहुँचना firstPromise
बेर्गी

6

एक और जवाब, babel-nodeसंस्करण <6 का उपयोग कर

का उपयोग करते हुए async - await

npm install -g babel@5.6.14

example.js:

async function getExample(){

  let response = await returnPromise();

  let response2 = await returnPromise2();

  console.log(response, response2)

}

getExample()

फिर, भागो babel-node example.jsऔर वॉइला!


1
हां, मैंने अपना पोस्ट करने के ठीक बाद किया। फिर भी, मैं इसे छोड़ने जा रहा हूं क्योंकि यह बताता है कि वास्तव में ईएस 7 का उपयोग करने के साथ कैसे उठना और दौड़ना है क्योंकि यह कहना है कि किसी दिन ईएस 7 उपलब्ध होगा।
एंथनी

1
ओह ठीक है, मुझे यह कहने के लिए अपने जवाब को अपडेट करना चाहिए कि इन के लिए "प्रयोगात्मक" प्लगइन्स पहले से ही यहां हैं।
बरगी

2

मैं अपने स्वयं के कोड में इस पैटर्न का उपयोग नहीं करने जा रहा हूं क्योंकि मैं वैश्विक चर का उपयोग करने का बड़ा प्रशंसक नहीं हूं। हालांकि, एक चुटकी में यह काम करेगा।

उपयोगकर्ता एक प्रचारित मॉडल है।

var globalVar = '';

User.findAsync({}).then(function(users){
  globalVar = users;
}).then(function(){
  console.log(globalVar);
});

2
सूचना है कि इस पैटर्न पहले से ही में विस्तृत है परिवर्त्य प्रासंगिक राज्य जवाब (और यह भी कारण है कि यह बदसूरत है - मैं नहीं एक बड़े प्रशंसक या तो हूँ)
Bergi

आपके मामले में, पैटर्न हालांकि बेकार लगता है। तुम globalVarसब पर एक की जरूरत नहीं है , बस User.findAsync({}).then(function(users){ console.log(users); mongoose.connection.close() });?
बेर्गी

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

2

अनुक्रमिक निष्पादक nsynjs का उपयोग करते हुए एक और उत्तर :

function getExample(){

  var response1 = returnPromise1().data;

  // promise1 is resolved at this point, '.data' has the result from resolve(result)

  var response2 = returnPromise2().data;

  // promise2 is resolved at this point, '.data' has the result from resolve(result)

  console.log(response, response2);

}

nynjs.run(getExample,{},function(){
    console.log('all done');
})

अद्यतन: काम कर उदाहरण जोड़ा गया

function synchronousCode() {
     var urls=[
         "https://ajax.googleapis.com/ajax/libs/jquery/1.7.0/jquery.min.js",
         "https://ajax.googleapis.com/ajax/libs/jquery/1.8.0/jquery.min.js",
         "https://ajax.googleapis.com/ajax/libs/jquery/1.9.0/jquery.min.js"
     ];
     for(var i=0; i<urls.length; i++) {
         var len=window.fetch(urls[i]).data.text().data.length;
         //             ^                   ^
         //             |                   +- 2-nd promise result
         //             |                      assigned to 'data'
         //             |
         //             +-- 1-st promise result assigned to 'data'
         //
         console.log('URL #'+i+' : '+urls[i]+", length: "+len);
     }
}

nsynjs.run(synchronousCode,{},function(){
    console.log('all done');
})
<script src="https://rawgit.com/amaksr/nsynjs/master/nsynjs.js"></script>


1

ब्लूबर्ड का उपयोग करते समय, आप .bindवादा श्रृंखला में चर साझा करने के लिए विधि का उपयोग कर सकते हैं :

somethingAsync().bind({})
.spread(function (aValue, bValue) {
    this.aValue = aValue;
    this.bValue = bValue;
    return somethingElseAsync(aValue, bValue);
})
.then(function (cValue) {
    return this.aValue + this.bValue + cValue;
});

अधिक जानकारी के लिए कृपया इस लिंक को देखें:

http://bluebirdjs.com/docs/api/promise.bind.html


सूचना है कि इस पैटर्न पहले से ही में विस्तृत है परिवर्त्य प्रासंगिक राज्य जवाब
Bergi


1

मुझे लगता है कि आप RSVP के हैश का उपयोग कर सकते हैं।

नीचे जैसा कुछ है:

    const mainPromise = () => {
        const promise1 = new Promise((resolve, reject) => {
            setTimeout(() => {
                console.log('first promise is completed');
                resolve({data: '123'});
            }, 2000);
        });

        const promise2 = new Promise((resolve, reject) => {
            setTimeout(() => {
                console.log('second promise is completed');
                resolve({data: '456'});
            }, 2000);
        });

        return new RSVP.hash({
              prom1: promise1,
              prom2: promise2
          });

    };


   mainPromise()
    .then(data => {
        console.log(data.prom1);
        console.log(data.prom2);
    });

हां, यह रूप में एक ही है समाधान , केवल एक वस्तु के बजाय एक सरणी के साथ। Promise.all
बेर्गी

0

समाधान:

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

यहाँ एक पूर्ण उदाहरण है:

// Get info asynchronously from a server
function pGetServerInfo()
    {
    // then value: "server info"
    } // pGetServerInfo

// Write into a file asynchronously
function pWriteFile(path,string)
    {
    // no then value
    } // pWriteFile

// The heart of the solution: Write formatted info into a log file asynchronously,
// using the pGetServerInfo and pWriteFile operations
function pLogInfo(localInfo)
    {
    var scope={localInfo:localInfo}; // Create an explicit scope object
    var thenFunc=p2.bind(scope); // Create a temporary function with this scope
    return (pGetServerInfo().then(thenFunc)); // Do the next 'then' in the chain
    } // pLogInfo

// Scope of this 'then' function is {localInfo:localInfo}
function p2(serverInfo)
    {
    // Do the final 'then' in the chain: Writes "local info, server info"
    return pWriteFile('log',this.localInfo+','+serverInfo);
    } // p2

इस समाधान को निम्नानुसार लगाया जा सकता है:

pLogInfo("local info").then().catch(err);

(नोट: इस समाधान का एक अधिक जटिल और पूर्ण संस्करण परीक्षण किया गया है, लेकिन यह उदाहरण संस्करण नहीं है, इसलिए इसमें बग हो सकता है।)


यह घोंसले के शिकार के रूप में एक ही पैटर्न लगता है (और) उत्तर बंद कर देता है
Bergi

यह समान दिखता है। जब से मुझे पता चला है कि नए Async / Await सिंटैक्स में तर्कों का स्वचालित बंधन शामिल है, इसलिए सभी तर्क सभी अतुल्यकालिक कार्यों के लिए उपलब्ध हैं। मैं वादा छोड़ रहा हूं।
डेविड स्पेक्टर

async/ awaitअभी भी वादों का उपयोग करने का मतलब है। आप जो त्याग कर सकते हैं वह thenकॉलबैक के साथ कॉल है।
बर्गी

-1

मैं वादों के बारे में जो कुछ भी सीखता हूं, उसका उपयोग केवल तभी करना है जब रिटर्न मान संभव हो तो उन्हें संदर्भित करने से बचें । async / प्रतीक्षा सिंटैक्स इसके लिए विशेष रूप से व्यावहारिक है। आज सभी नवीनतम ब्राउज़र और नोड इसका समर्थन करते हैं: https://caniuse.com/#feat=async-functions , एक सरल व्यवहार है और कोड तुल्यकालिक कोड पढ़ने की तरह है, कॉलबैक के बारे में भूल जाओ ...

ऐसे मामलों में जब मुझे वादों का संदर्भ देने की आवश्यकता होती है, जब निर्माण और संकल्प स्वतंत्र / नहीं-संबंधित स्थानों पर होते हैं। इसलिए एक कृत्रिम संघ और शायद एक घटना के श्रोता के बजाय "दूर" वादे को हल करने के लिए, मैं वादा को एक आस्थगित के रूप में उजागर करना पसंद करता हूं, जो निम्नलिखित कोड इसे मान्य es5 में लागू करता है

/**
 * Promise like object that allows to resolve it promise from outside code. Example:
 *
```
class Api {
  fooReady = new Deferred<Data>()
  private knower() {
    inOtherMoment(data=>{
      this.fooReady.resolve(data)
    })
  }
}
```
 */
var Deferred = /** @class */ (function () {
  function Deferred(callback) {
    var instance = this;
    this.resolve = null;
    this.reject = null;
    this.status = 'pending';
    this.promise = new Promise(function (resolve, reject) {
      instance.resolve = function () { this.status = 'resolved'; resolve.apply(this, arguments); };
      instance.reject = function () { this.status = 'rejected'; reject.apply(this, arguments); };
    });
    if (typeof callback === 'function') {
      callback.call(this, this.resolve, this.reject);
    }
  }
  Deferred.prototype.then = function (resolve) {
    return this.promise.then(resolve);
  };
  Deferred.prototype.catch = function (r) {
    return this.promise.catch(r);
  };
  return Deferred;
}());

मेरा एक टाइपस्क्रिप्ट प्रोजेक्ट ट्रांसप्लड फॉर्म:

https://github.com/cancerberoSgx/misc-utils-of-mine/blob/2927c2477839f7b36247d054e7e50abe8a41358b/misc-utils-of-mine-generic/src/promise.ts#L31

अधिक जटिल मामलों के लिए, मैं अक्सर परीक्षण और टाइप किए बिना निर्भरता के बिना इन आदमी छोटे वादे उपयोगिताओं का उपयोग करता हूं। पी-मैप कई बार उपयोगी रहा है। मुझे लगता है कि उन्होंने सर्वाधिक उपयोग के मामलों को कवर किया:

https://github.com/sindresorhus?utf8=%E2%9C%93&tab=repositories&q=promise&type=source&language=


लगता है जैसे आप या तो परस्पर संदर्भात्मक अवस्था या समकालिक निरीक्षण का सुझाव दे रहे हैं ?
बरगी

@bergi पहली बार मैं उन नामों को प्रमुखता देता हूं। सूची में धन्यवाद। मैं इस तरह के स्व-जागरूक वादों को जानता हूं, जिन्हें Deferred के नाम से जाना जाता है - BTW कार्यान्वयन केवल एक वादा है जिसे लपेटा गया है। मुझे अक्सर उन मामलों में इस पैटर्न की आवश्यकता होती है जहां वादे के निर्माण और संकल्प की जिम्मेदारी स्वतंत्र होती है, इसलिए उन्हें केवल एक वादे को हल करने के लिए संबंधित होने की कोई आवश्यकता नहीं है। मैंने अनुकूलित किया लेकिन आपके उदाहरण के लिए नहीं, और एक वर्ग का उपयोग करके, लेकिन शायद समकक्ष।
कर्क राशि
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.