javascript - मैं पिछले वादे परिणामों को.then() श्रृंखला में कैसे एक्सेस करूं?




scope promise (10)

मैंने अपने कोड को promises के लिए पुनर्गठित किया promises , और एक अद्भुत लंबी फ्लैट वादा श्रृंखला का निर्माण किया है , जिसमें कई .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?
    });
}

"उत्परिवर्तनीय प्रासंगिक राज्य" पर एक कम कठोर स्पिन

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

function getExample(){
    //locally scoped
    const results = {};
    return promiseA(...).then(function(resultA){
        results.a = resultA;
        return promiseB(...);
    }).then(function(resultB){
        results.b = resultB;
        return promiseC(...);
    }).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 फ़ंक्शन के प्रत्येक आमंत्रण के लिए बनाया गया है।

ईसीएमएस्क्रिप्ट सद्भावना

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

ईसीएमएस्क्रिप्ट 8

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

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

ईसीएमएस्क्रिप्ट 6

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

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

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 के बाद से नोड.जेएस में काम करता था, कुछ ब्राउज़रों (या उनके देव संस्करण) ने अपेक्षाकृत जल्दी जेनरेटर सिंटैक्स का समर्थन किया था।

ईसीएमएस्क्रिप्ट 5

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

और फिर, कई अन्य संकलन-से-जेएस भाषाएं भी हैं जो एसिंक्रोनस प्रोग्रामिंग को आसान बनाने के लिए समर्पित हैं। वे आम तौर पर await करने के समान वाक्यविन्यास का उपयोग करते हैं, (जैसे आईस्ड कॉफीस्क्रिप्ट ), लेकिन ऐसे अन्य भी हैं जो हास्केल जैसी डॉट-नोटेशन (जैसे LatteJs , PureScript , PureScript या LispyScript ) की सुविधा देते हैं।


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

चर के दायरे को बनाए रखने के लिए बंद करने का उपयोग करना (हमारे मामले में, सफलता कॉलबैक फ़ंक्शन पैरामीटर) प्राकृतिक जावास्क्रिप्ट समाधान है। वादे के साथ, हम मनमाने ढंग से घोंसला और चपटा कर सकते हैं। .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
    };
}

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

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
}

तुल्यकालिक निरीक्षण

चर के लिए वादे-बाद-आवश्यक-मूल्यों को आवंटित करना और फिर सिंक्रोनस निरीक्षण के माध्यम से अपना मूल्य प्राप्त करना। उदाहरण ब्लूबर्ड की .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();
    });
}

स्पष्ट पास-थ्रू

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

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 पर बंद हो resultA , और अगले चरण में दोनों परिणामों की एक सरणी पास करता है। जो एक ही चर में इसे तोड़ने के लिए पैरामीटर विनाशकारी वाक्यविन्यास का उपयोग करता है।

.spread() साथ विनाशकारी उपलब्ध होने से पहले, एक निफ्टी सहायक विधि जिसे .spread() कहा जाता है, कई वादे पुस्तकालयों ( Q , Bluebird , when , ...) द्वारा प्रदान किया गया था। यह एकाधिक पैरामीटर के साथ एक फ़ंक्शन लेता है - प्रत्येक सरणी तत्व के लिए - एक .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
    });
}

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


अनुक्रमिक निष्पादक 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>


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

async - await का उपयोग async - await

npm install -g [email protected]

example.js:

async function getExample(){

  let response = await returnPromise();

  let response2 = await returnPromise2();

  console.log(response, response2)

}

getExample()

फिर, babel-node example.js और voila चलाएं!


नोड 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

के रूप में सरल हो सकता है!


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

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

    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);
    });

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

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

var globalVar = '';

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





es6-promise