आप एक वादे से कई मूल्यों को कैसे ठीक से वापस करते हैं?


86

मैंने हाल ही में एक निश्चित स्थिति में एक-दो बार भाग लिया है, जो मुझे नहीं पता था कि कैसे ठीक से हल करना है। निम्नलिखित कोड मान लें:

somethingAsync()
  .then( afterSomething )
  .then( afterSomethingElse )

function afterSomething( amazingData ) {
  return processAsync( amazingData );
}
function afterSomethingElse( processedData ) {
}

अब एक ऐसी स्थिति पैदा होती है, जहां मैं के लिए उपयोग किया चाहेगा सकता है amazingDataमें afterSomethingElse

एक स्पष्ट समाधान एक सरणी या एक हैश को वापस करना होगा afterSomething, क्योंकि, ठीक है, आप केवल एक फ़ंक्शन से एक मान वापस कर सकते हैं। लेकिन मैं सोच रहा था कि वहाँ afterSomethingElse2 मापदंडों को स्वीकार करने और इसे वैसे ही लागू करने का तरीका है , क्योंकि यह दस्तावेज़ और समझने में बहुत आसान लगता है।

मैं केवल इस संभावना के बारे में सोच रहा हूँ क्योंकि वहाँ है Q.spread, जो मैं चाहता हूँ के समान कुछ करता है।




ईएस 6 में डी-स्ट्रक्चरिंग असाइनमेंट से मदद मिलेगी। जाँच यहाँ
रवि तेजा

जवाबों:


88

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

एक वादा स्वाभाविक रूप से एक मूल्य के साथ हल होता है - यह इस बात का हिस्सा है कि क्यू कैसे काम करता है, कैसे वादे / ए + कल्पना काम करता है और कैसे अमूर्त काम करता है।

निकटतम आप उपयोग कर सकते हैं Q.spreadऔर सरणियाँ दे सकते हैं या ES6 विनाशकारी का उपयोग कर सकते हैं यदि यह समर्थित है या आप BabelJS जैसे ट्रांसप्लीकेशन टूल का उपयोग करने के इच्छुक हैं।

एक वादे की श्रृंखला के संदर्भ में नीचे जाने के लिए कृपया उस पर बर्गी की उत्कृष्ट विहित का उल्लेख करें ।


16
एक गुण के साथ हल करने में क्या गलत है जिसमें कई गुण हैं? एक संकल्प से कई मूल्यों को प्राप्त करने के लिए एक सरल तरीके की तरह लगता है।
jfriend00

6
ऐसा करना पूरी तरह से ठीक है
बेंजामिन ग्रुएनबाम

आप .spread()इस संबंधित उत्तर में ब्लूबर्ड को दिखाने का वादा भी कर सकते हैं : stackoverflow.com/a/22776850/1624862
केविन घडानी

Promise.all () का व्यवहार इसके विपरीत लगता है। Promise.all([a, b, c]).then(function(x, y, z) {...})एक्स, वाई और जेड के साथ सभी आधुनिक जावास्क्रिप्ट इंजनों में सही ढंग से काम करता है, ए, बी और सी के सुलझे हुए मूल्यों का मूल्यांकन करता है। इसलिए यह कहना अधिक सटीक है कि भाषा आपको उपयोगकर्ता कोड से आसानी से (या सान) नहीं होने देती है (क्योंकि आप सीधे किसी तबके से एक वादा वापस कर सकते हैं, आप अपने मूल्यों को वादों में लपेट सकते हैं और फिर वादे के साथ लपेट सकते हैं। .all () वांछित व्यवहार प्राप्त करने के लिए, एक दृढ़ तरीके से यद्यपि)।
ऑस्टिन हेमेलर्गरन

3
@AustinHemmelgarn यह सिर्फ गलत है, एक सरणी के साथPromise.all पूरा करता है । में है । इसलिए आपको ऐसा करने की आवश्यकता है जो एक विनाशकारी कार्य हैPromise.all([a,b]).then((a, b) => bundefined.then(([a, b]) =>
बेंजामिन ग्रुएनबाम

38

आप केवल एक मान पास कर सकते हैं, लेकिन यह उदाहरण के रूप में गुणक मानों के साथ एक सरणी हो सकता है:

function step1(){
  let server = "myserver.com";
  let data = "so much data, very impresive";
  return Promise.resolve([server, data]);
}

दूसरी तरफ, आप व्यक्तिगत मान प्राप्त करने के लिए ES2015 के लिए विनाशकारी अभिव्यक्ति का उपयोग कर सकते हैं ।

function step2([server, data]){
  console.log(server); // print "myserver.com"
  console.log(data);   // print "so much data, very impresive"
  return Promise.resolve("done");
}

दोनों वादों को पूरा करने के लिए, उनका पीछा करना:

step1()
.then(step2)
.then((msg)=>{
  console.log(msg); // print "done"
})

5
इसे विनाशकारी कहा जाता है , न कि "डिकंस्ट्रक्टर", और यह एक ऑपरेटर नहीं है: - /
बर्गी

3
Btw, आप मापदंडों में विनाशकारी सही का उपयोग कर सकते हैं: function step2([server, data]) { …- इस तरह आप भी अंतर्निहित ग्लोबल्स को असाइन करने से बचते हैं। और आपको वास्तव में अपने उदाहरणों में निर्माता का उपयोग करना चाहिए returnया Promise.resolveनहीं new Promise
बरगी

थैंक्यू @Bergi for the recomendations!
एलेजांद्रो सिल्वा

19

आप दोनों मानों वाले ऑब्जेक्ट को वापस कर सकते हैं - इसमें कुछ भी गलत नहीं है।

एक और रणनीति मूल्य को बंद रखने के माध्यम से रखना है, इसके बजाय इसे पारित करने के बजाय:

somethingAsync().then(afterSomething);

function afterSomething(amazingData) {
  return processAsync(amazingData).then(function (processedData) {
    // both amazingData and processedData are in scope here
  });
}

पूरी तरह से आंशिक रूप से इनलेटेड फॉर्म (समकक्ष, यकीनन अधिक सुसंगत) के बजाय:

somethingAsync().then(function (amazingData) {
  return processAsync(amazingData).then(function (processedData) {
    // both amazingData and processedData are in scope here
  });
}

3
एक thenदूसरे के अंदर लौटना ठीक है then? यह एक विरोधी पैटर्न नहीं है ?
robe007

जैसे @ robe007 ने कहा, क्या यह 'कॉलबैक नरक' के समान नहीं होगा? यहाँ आपके घोंसले के शिकार कॉलबैक कार्यों के बजाय ब्लॉक करता है, इससे वादों के बहुत उद्देश्य को पराजित किया जाएगा
धीरज

5

दो चीजें जो आप कर सकते हैं, एक वस्तु वापस करें

somethingAsync()
    .then( afterSomething )
    .then( afterSomethingElse );

function processAsync (amazingData) {
     //processSomething
     return {
         amazingData: amazingData, 
         processedData: processedData
     };
}

function afterSomething( amazingData ) {
    return processAsync( amazingData );
}

function afterSomethingElse( dataObj ) {
    let amazingData = dataObj.amazingData,
        processedData = dataObj.proccessedData;
}

गुंजाइश का उपयोग करें!

var amazingData;
somethingAsync()
  .then( afterSomething )
  .then( afterSomethingElse )

function afterSomething( returnedAmazingData ) {
  amazingData = returnedAmazingData;
  return processAsync( amazingData );
}
function afterSomethingElse( processedData ) {
  //use amazingData here
}

3

यहां बताया गया है कि मैं आपको कैसे करना चाहिए।

श्रृंखला को विभाजित करना

क्योंकि दोनों फंक्शन amazingData का उपयोग कर रहे होंगे , यह उन्हें एक समर्पित फ़ंक्शन में रखने के लिए समझ में आता है। मैं आमतौर पर ऐसा करता हूं कि हर बार मैं कुछ डेटा का पुन: उपयोग करना चाहता हूं, इसलिए यह हमेशा एक फ़ंक्शन arg के रूप में मौजूद होता है।

जैसा कि आपका उदाहरण कुछ कोड चला रहा है, मुझे लगता है कि यह सब एक फ़ंक्शन के अंदर घोषित किया जाएगा। मैं इसे टोटो () कहूंगा । फिर हमारे पास एक और फंक्शन होगा जो afterSomething () और afterSomethingElse () दोनों चलेगा

function toto() {
    return somethingAsync()
        .then( tata );
}

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

अब यह नया कार्य क्या होगा जैसा आमतौर पर निर्भर करता है कि processAsync () भी अतुल्यकालिक है ?

processAsync एसिंक्रोनस नहीं

यदि processAsync () अतुल्यकालिक नहीं है, तो चीजों को ओवरक्लम्प्लीकेट करने का कोई कारण नहीं। कुछ पुराने अच्छे अनुक्रमिक कोड इसे बनाएंगे।

function tata( amazingData ) {
    var processed = afterSomething( amazingData );
    return afterSomethingElse( amazingData, processed );
}

function afterSomething( amazingData ) {
    return processAsync( amazingData );
}
function afterSomethingElse( amazingData, processedData ) {
}

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

processAsync अतुल्यकालिक

यदि processAsync () असिंक्रोनस है, तो कोड थोड़ा अलग दिखेगा। यहाँ हम afterSomething () और afterSomethingElse () को कहीं और उपयोग नहीं करने जा रहे हैं।

function tata( amazingData ) {
    return afterSomething()
        .then( afterSomethingElse );

    function afterSomething( /* no args */ ) {
        return processAsync( amazingData );
    }
    function afterSomethingElse( processedData ) {
        /* amazingData can be accessed here */
    }
}

AfterSomethingElse () के लिए पहले जैसा है । यह अतुल्यकालिक हो सकता है या नहीं। एक वादा वापस कर दिया जाएगा, या एक सुलझे हुए वादे में लिपटे मूल्य।


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

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

खाने का उदाहरण

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

function goingThroughTheEatingProcess(plenty, of, args, to, match, real, life) {
    return iAmAsync()
        .then(chew)
        .then(swallow);

        function chew(result) {
            return carefullyChewThis(plenty, of, args, "water", "piece of tooth", result);
        }

        function swallow(wine) {
            return nowIsTimeToSwallow(match, real, life, wine);
        }
}

function iAmAsync() {
    return Promise.resolve("mooooore");
}

function carefullyChewThis(plenty, of, args, and, some, more) {
    return true;
}

function nowIsTimeToSwallow(match, real, life, bobool) {
}

Promise.resolve () पर बहुत अधिक ध्यान न दें । यह बस एक त्वरित तरीका है एक सुलझा हुआ वादा करने का। इसके द्वारा मैं जो भी हासिल करने की कोशिश करता हूं वह सभी कोड मैं एक ही स्थान पर चल रहा है - केवल थेन के नीचे । अधिक वर्णनात्मक नाम के साथ अन्य सभी कार्य पुन: प्रयोज्य हैं।

इस तकनीक के साथ दोष यह है कि यह बहुत सारे कार्यों को परिभाषित कर रहा है। लेकिन यह एक आवश्यक दर्द है जिससे मैं डरता हूं ताकि सभी जगहों पर अनाम कार्य न हो सकें। और वैसे भी जोखिम क्या है: एक ढेर अतिप्रवाह? (मज़ाक!)


अन्य उत्तरों में परिभाषित सरणियों या वस्तुओं का उपयोग करना भी कारगर होगा। यह एक तरह से केविन रीड द्वारा प्रस्तावित उत्तर है

आप bind () या Promise.all () का भी उपयोग कर सकते हैं । ध्यान दें कि उन्हें अभी भी आपको अपना कोड विभाजित करने की आवश्यकता होगी।

बाँध का उपयोग करना

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

function tata( amazingData ) {
    return afterSomething( amazingData )
        .then( afterSomethingElse.bind(null, amazingData) );
}

function afterSomething( amazingData ) {
    return processAsync( amazingData );
}
function afterSomethingElse( amazingData, processedData ) {
}

इसे सरल रखने के लिए, बाँध () फ़ंक्शन के लिए आर्ग्स की सूची (पहले एक को छोड़कर ) को प्रीपेन्ड करेगा जब इसे कहा जाता है।

Promise.all का उपयोग करना

अपनी पोस्ट में आपने प्रसार () का उपयोग करने का उल्लेख किया है । मैंने आपके द्वारा उपयोग किए जा रहे ढांचे का उपयोग कभी नहीं किया, लेकिन यहां बताया गया है कि आपको इसका उपयोग करने में कैसे सक्षम होना चाहिए।

कुछ का मानना ​​है कि Promise.all () सभी समस्याओं का समाधान है, इसलिए यह मेरे अनुमान के अनुसार उल्लेख के योग्य है।

function tata( amazingData ) {
    return Promise.all( [ amazingData, afterSomething( amazingData ) ] )
        .then( afterSomethingElse );
}

function afterSomething( amazingData ) {
    return processAsync( amazingData );
}
function afterSomethingElse( args ) {
    var amazingData = args[0];
    var processedData = args[1];
}

आप Promise.all को डेटा पास कर सकते हैं () - सरणी की उपस्थिति पर ध्यान दें - जब तक वादे नहीं होंगे, लेकिन सुनिश्चित करें कि कोई भी वादा विफल नहीं होता है अन्यथा यह प्रसंस्करण बंद कर देगा।

और बदले से नए चर को परिभाषित करने की आर्ग तर्क, आप का उपयोग करने के लिए सक्षम होना चाहिए ) प्रसार ( बजाय तो () भयानक काम के सभी तरह के लिए।


3

बस एक वस्तु बनाएं और उस वस्तु से तर्क निकालें।

let checkIfNumbersAddToTen = function (a, b) {
return new Promise(function (resolve, reject) {
 let c = parseInt(a)+parseInt(b);
 let promiseResolution = {
     c:c,
     d : c+c,
     x : 'RandomString'
 };
 if(c===10){
     resolve(promiseResolution);
 }else {
     reject('Not 10');
 }
});
};

वादा से बहस खींचो।

checkIfNumbersAddToTen(5,5).then(function (arguments) {
console.log('c:'+arguments.c);
console.log('d:'+arguments.d);
console.log('x:'+arguments.x);
},function (failure) {
console.log(failure);
});

2

आप जो भी वादे से लौटते हैं उसे अगले .then()चरण में अलिखित होने के वादे में लपेट दिया जाएगा ।

यह दिलचस्प हो जाता है जब आपको एक या अधिक प्रतिसाद (जैसे) के साथ एक या अधिक वादे वापस करने की आवश्यकता होती है;

Promise.resolve([Promise.resolve(1), Promise.resolve(2), 3, 4])
       .then(([p1,p2,n1,n2]) => /* p1 and p2 are still promises */);

इन मामलों में यह आवश्यक होगा कि वे अगले चरण जैसे कि अपरिवर्तित Promise.all()हो p1और प्राप्त करने के लिए उपयोग करेंp2.then()

Promise.resolve(Promise.all([Promise.resolve(1), Promise.resolve(2), 3, 4]))
       .then(([p1,p2,n1,n2]) => /* p1 is 1, p2 is 2, n1 is 3 and n2 is 4 */);

1

आप देख सकते हैं प्रत्यक्ष द्वारा प्रतिनिधित्व Rxjs , तुम वापस की सुविधा देता है एक से अधिक मूल्य।


0

बस एक टपल लौटें:

async add(dto: TDto): Promise<TDto> {
console.log(`${this.storeName}.add(${dto})`);
return firebase.firestore().collection(this.dtoName)
  .withConverter<TDto>(this.converter)
  .add(dto)
  .then(d => [d.update(this.id, d.id), d.id] as [any, string])
  .then(x => this.get(x[1]));

}

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.