वादा श्रृंखला में कई कैच को संभालना


125

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

तो उदाहरण के लिए मेरे पास एक एक्सप्रेस ऐप के भीतर एक वादा श्रृंखला है जैसे:

repository.Query(getAccountByIdQuery)
        .catch(function(error){
            res.status(404).send({ error: "No account found with this Id" });
        })
        .then(convertDocumentToModel)
        .then(verifyOldPassword)
        .catch(function(error) {
            res.status(406).send({ OldPassword: error });
        })
        .then(changePassword)
        .then(function(){
            res.status(200).send();
        })
        .catch(function(error){
            console.log(error);
            res.status(500).send({ error: "Unable to change password" });
        });

इसलिए मैं जो व्यवहार कर रहा हूं, वह है:

  • आईडी द्वारा खाता प्राप्त करने के लिए जाता है
  • यदि इस बिंदु पर अस्वीकृति है, तो बम विस्फोट करें और एक त्रुटि लौटाएं
  • यदि कोई त्रुटि नहीं है, तो दस्तावेज़ को मॉडल में लौटाएं
  • डेटाबेस दस्तावेज़ के साथ पासवर्ड को सत्यापित करें
  • यदि पासवर्ड मेल नहीं खाते हैं तो बम विस्फोट करें और एक अलग त्रुटि लौटाएं
  • यदि कोई त्रुटि नहीं है तो पासवर्ड बदलें
  • फिर सफलता लौटाते हैं
  • अगर कुछ और गलत हुआ, तो 500 वापस करो

इसलिए वर्तमान में कैच को रोकना प्रतीत नहीं होता है, और यह समझ में आता है, इसलिए मैं सोच रहा हूं कि क्या मेरे लिए कोई रास्ता है जो त्रुटियों के आधार पर किसी निश्चित बिंदु पर रुकने के लिए चेन को मजबूर कर सकता है, या यदि कोई बेहतर तरीका है के रूप में वहाँ के रूप में शाखाओं में बंटी व्यवहार के कुछ फार्म पाने के लिए इस संरचना के लिए if X do Y else Z

कोई भी मदद बहुत अच्छी रहेगी।


क्या आप या तो वापस ला सकते हैं या जल्दी लौट सकते हैं?
Pieter21

जवाबों:


126

यह व्यवहार बिल्कुल एक तुल्यकालिक फेंक की तरह है:

try{
    throw new Error();
} catch(e){
    // handle
} 
// this code will run, since you recovered from the error!

यह उस बिंदु का आधा है .catch- त्रुटियों से उबरने में सक्षम होना। यह संकेत देना वांछनीय हो सकता है कि राज्य अभी भी एक त्रुटि है।

try{
    throw new Error();
} catch(e){
    // handle
    throw e; // or a wrapper over e so we know it wasn't handled
} 
// this code will not run

हालाँकि, यह अकेले आपके मामले में काम नहीं करेगा क्योंकि त्रुटि बाद में हैंडलर द्वारा पकड़ ली जाएगी। यहाँ वास्तविक मुद्दा यह है कि सामान्यीकृत "हैंडल एनीथिंग" त्रुटि हैंडलर सामान्य रूप से एक बुरा अभ्यास हैं और अन्य प्रोग्रामिंग भाषाओं और पारिस्थितिक तंत्रों पर बहुत अधिक प्रभावित होते हैं। इस कारण से ब्लूबर्ड टाइप और प्रेडिकेट कैच ऑफर करता है।

अतिरिक्त लाभ यह है कि आपके व्यावसायिक तर्क को अनुरोध (प्रतिक्रिया चक्र) के बारे में बिल्कुल भी जानकारी नहीं होनी चाहिए। यह निर्णय लेने की ज़िम्मेदारी नहीं है कि क्लाइंट को कौन सी HTTP स्थिति और त्रुटि मिलती है और बाद में जैसे ही आपका ऐप बढ़ता है, आप व्यवसाय तर्क को अलग करना चाहते हैं (आप अपने डीबी को कैसे क्वेरी करें और अपने डेटा को कैसे संसाधित करें) जो आप क्लाइंट को भेजते हैं। (क्या http स्थिति कोड, क्या पाठ और क्या प्रतिक्रिया)।

यहां बताया गया है कि मैं आपका कोड कैसे लिखूंगा।

सबसे पहले, मैं .Queryएक फेंक देना NoSuchAccountErrorचाहता हूं, मैं इसे उप-वर्ग बना Promise.OperationalErrorदूंगा जिसमें से ब्लूबर्ड पहले से ही प्रदान करता है। यदि आप अनिश्चित हैं कि किसी त्रुटि को कैसे रेखांकित किया जाए तो मुझे बताएं।

मैं अतिरिक्त रूप से इसके लिए उप-सूची बनाऊंगा AuthenticationErrorऔर फिर कुछ ऐसा करूंगा :

function changePassword(queryDataEtc){ 
    return repository.Query(getAccountByIdQuery)
                     .then(convertDocumentToModel)
                     .then(verifyOldPassword)
                     .then(changePassword);
}

जैसा कि आप देख सकते हैं - यह बहुत साफ है और आप इस प्रक्रिया में क्या होता है के निर्देश पुस्तिका की तरह पाठ पढ़ सकते हैं। इसे भी अनुरोध / प्रतिक्रिया से अलग किया गया है।

अब, मैं इसे रूट हैंडलर से इस तरह से कॉल करूंगा:

 changePassword(params)
 .catch(NoSuchAccountError, function(e){
     res.status(404).send({ error: "No account found with this Id" });
 }).catch(AuthenticationError, function(e){
     res.status(406).send({ OldPassword: error });
 }).error(function(e){ // catches any remaining operational errors
     res.status(500).send({ error: "Unable to change password" });
 }).catch(function(e){
     res.status(500).send({ error: "Unknown internal server error" });
 });

इस तरह, लॉजिक सभी एक जगह है और क्लाइंट को त्रुटियों को कैसे हैंडल करना है, इसका निर्णय सभी एक ही स्थान पर करते हैं और वे किसी भी तरह से परेशान नहीं होते हैं।


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

1
मैंने सोचा कि "यह .catch के बिंदु का आधा है - त्रुटियों से उबरने में सक्षम होने के लिए" यह स्पष्ट है लेकिन आगे स्पष्ट करने के लिए धन्यवाद कि यह एक अच्छा उदाहरण है।
बेंजामिन ग्रुएनबाउम

1
यदि ब्लूबर्ड का उपयोग नहीं किया जा रहा है तो क्या होगा? सादे es6 वादों में केवल एक स्ट्रिंग त्रुटि संदेश होता है जिसे पकड़ने के लिए पास किया जाता है।
चौकीदार

3
ES6 वादों के साथ @clocksmith आप सब कुछ पकड़ रहे हैं और instanceofमैन्युअल रूप से खुद को धोखा दे रहे हैं ।
बेंजामिन ग्रुएनबाम

1
सबक्लासिंग त्रुटि वस्तुओं के संदर्भ की तलाश करने वालों के लिए ऑब्जेक्ट्स ब्लूबर्डज्स . com/docs/api/catch.html#filtered-catch पढ़ें । अनुच्छेद बहुत अधिक यहाँ दिए गए कई कैच उत्तर को पुन: पेश करता है।
मम्मीबोट

47

.catchtry-catchकथन की तरह काम करता है , जिसका अर्थ है कि आपको अंत में केवल एक कैच की आवश्यकता है:

repository.Query(getAccountByIdQuery)
        .then(convertDocumentToModel)
        .then(verifyOldPassword)
        .then(changePassword)
        .then(function(){
            res.status(200).send();
        })
        .catch(function(error) {
            if (/*see if error is not found error*/) {
                res.status(404).send({ error: "No account found with this Id" });
            } else if (/*see if error is verification error*/) {
                res.status(406).send({ OldPassword: error });
            } else {
                console.log(error);
                res.status(500).send({ error: "Unable to change password" });
            }
        });

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

8
क्या यह की कीमत के लिए @Grofit - Bluebird में टाइप कैच थे Petka (Esailija) के विचार के साथ :) उसे समझा दिया है कि वे एक बेहतर दृष्टिकोण यहाँ हो कोई ज़रूरत नहीं शुरू करने के लिए। मुझे लगता है कि वह आपको भ्रमित नहीं करना चाहता था क्योंकि JS के बहुत सारे लोग अवधारणा के बारे में बहुत जागरूक नहीं हैं।
बेंजामिन ग्रुएनबाउम

17

मैं सोच रहा था कि क्या मेरे लिए कोई रास्ता है जो त्रुटियों के आधार पर किसी निश्चित बिंदु पर श्रृंखला को रोकने के लिए मजबूर कर सकता है

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

उनके पैटर्न की व्युत्पत्ति त्रुटि प्रकारों को भेद करने के लिए नहीं होगी, बल्कि उन त्रुटियों का उपयोग करने के लिए होगी जिनके पास एक statusCodeऔर bodyसामान्य .catchहैंडलर से भेजे जाने वाले फ़ील्ड हैं । आपकी एप्लिकेशन संरचना के आधार पर, उसका समाधान हालांकि क्लीनर हो सकता है।

या अगर वहाँ एक बेहतर तरीका है यह शाखाओं में बंटी व्यवहार के कुछ फार्म प्राप्त करने के लिए है

हां, आप वादों के साथ शाखा कर सकते हैं । हालाँकि, इसका मतलब श्रृंखला को छोड़ना और "वापस जाना" है, घोंसले के शिकार करना - जैसे आप एक नेस्टेड में करते हैं, यदि और-या कथन को पकड़ें:

repository.Query(getAccountByIdQuery)
.then(function(account) {
    return convertDocumentToModel(account)
    .then(verifyOldPassword)
    .then(function(verification) {
        return changePassword(verification)
        .then(function() {
            res.status(200).send();
        })
    }, function(verificationError) {
        res.status(406).send({ OldPassword: error });
    })
}, function(accountError){
    res.status(404).send({ error: "No account found with this Id" });
})
.catch(function(error){
    console.log(error);
    res.status(500).send({ error: "Unable to change password" });
});

5

मैं इस तरह से कर रहा हूँ:

आप अंत में अपना कैच छोड़ें। और जब आपकी श्रृंखला मध्य में होती है, तो बस एक त्रुटि फेंक दें।

    repository.Query(getAccountByIdQuery)
    .then((resultOfQuery) => convertDocumentToModel(resultOfQuery)) //inside convertDocumentToModel() you check for empty and then throw new Error('no_account')
    .then((model) => verifyOldPassword(model)) //inside convertDocumentToModel() you check for empty and then throw new Error('no_account')        
    .then(changePassword)
    .then(function(){
        res.status(200).send();
    })
    .catch((error) => {
    if (error.name === 'no_account'){
        res.status(404).send({ error: "No account found with this Id" });

    } else  if (error.name === 'wrong_old_password'){
        res.status(406).send({ OldPassword: error });

    } else {
         res.status(500).send({ error: "Unable to change password" });

    }
});

आपके अन्य कार्य संभवतः इस तरह दिखेंगे:

function convertDocumentToModel(resultOfQuery) {
    if (!resultOfQuery){
        throw new Error('no_account');
    } else {
    return new Promise(function(resolve) {
        //do stuff then resolve
        resolve(model);
    }                       
}

4

पार्टी के लिए थोड़ा देर हो सकती है, लेकिन घोंसला बनाना संभव है .catch यहाँ दिखाए गए अनुसार है:

मोज़िला डेवलपर नेटवर्क - वादों का उपयोग करना

संपादित करें: मैंने इसे प्रस्तुत किया क्योंकि यह सामान्य रूप से पूछी गई कार्यक्षमता प्रदान करता है। हालाँकि यह इस विशेष मामले में नहीं है। क्योंकि जैसा कि पहले ही दूसरों द्वारा विस्तार से बताया गया है, .catchत्रुटि को पुनर्प्राप्त करने के लिए माना जाता है। उदाहरण के लिए, आप कई .catch कॉलबैक में क्लाइंट को कोई प्रतिक्रिया नहीं भेज सकते क्योंकि .catchकोई स्पष्ट रूप से उस मामले में इसे return हल नहीं करता है undefined, जिससे .thenट्रिगर करने के लिए आगे बढ़ना संभव है, हालांकि आपकी श्रृंखला वास्तव में हल नहीं होती है, संभवतः .catchट्रिगर करने और भेजने के लिए निम्नलिखित का कारण बनता है। ग्राहक को एक और प्रतिक्रिया, एक त्रुटि के कारण और संभावना UnhandledPromiseRejectionअपना रास्ता फेंक रही है । मुझे उम्मीद है कि इस सजा की सजा से आपको कुछ समझ में आएगा।


1
@AntonMenshov आप सही हैं। मैंने अपने उत्तर का विस्तार किया, यह समझाते हुए कि क्यों उनका वांछित व्यवहार अभी भी घोंसले के शिकार के साथ संभव नहीं है
denkquer

2

इसके बजाय .then().catch()...आप कर सकते हैं .then(resolveFunc, rejectFunc)। यह वादा श्रृंखला बेहतर होगा यदि आप रास्ते में चीजों को संभालते हैं। यहां बताया गया है कि मैं इसे कैसे लिखूंगा:

repository.Query(getAccountByIdQuery)
    .then(
        convertDocumentToModel,
        () => {
            res.status(404).send({ error: "No account found with this Id" });
            return Promise.reject(null)
        }
    )
    .then(
        verifyOldPassword,
        () => Promise.reject(null)
    )
    .then(
        changePassword,
        (error) => {
            if (error != null) {
                res.status(406).send({ OldPassword: error });
            }
            return Promise.Promise.reject(null);
        }
    )
    .then(
        _ => res.status(200).send(),
        error => {
            if (error != null) {
                console.error(error);
                res.status(500).send({ error: "Unable to change password" });
            }
        }
    );

नोट:if (error != null) एक हैक का एक सा हाल ही में त्रुटि के साथ बातचीत करने के लिए है।


1

मुझे लगता है कि बेंजामिन ग्रुएनबाम का जवाब एक जटिल तर्क अनुक्रम के लिए सबसे अच्छा समाधान है, लेकिन यहां सरल स्थितियों के लिए मेरा विकल्प है। मैं बस किसी भी बाद या बयानों को छोड़ने errorEncounteredके लिए एक ध्वज का उपयोग करता हूं । तो यह इस तरह दिखेगा:return Promise.reject()thencatch

let errorEncountered = false;
someCall({
  /* do stuff */
})
.catch({
  /* handle error from someCall*/
  errorEncountered = true;
  return Promise.reject();
})
.then({
  /* do other stuff */
  /* this is skipped if the preceding catch was triggered, due to Promise.reject */
})
.catch({
  if (errorEncountered) {
    return;
  }
  /* handle error from preceding then, if it was executed */
  /* if the preceding catch was executed, this is skipped due to the errorEncountered flag */
});

यदि आपके पास दो से अधिक हैं / जोड़े पकड़ते हैं, तो आपको शायद बेंजामिन ग्रुएनबाउम के समाधान का उपयोग करना चाहिए। लेकिन यह एक साधारण सेट-अप के लिए काम करता है।

ध्यान दें कि फाइनल catchकेवल इसके return;बजाय है return Promise.reject();, क्योंकि बाद में कोई भी thenऐसा नहीं है जिसे हमें छोड़ने की आवश्यकता है, और यह एक अनहेल्ड प्रॉमिस अस्वीकृति के रूप में गिना जाएगा, जिसे नोड पसंद नहीं करता है। जैसा कि ऊपर लिखा गया है, फाइनल catchशांतिपूर्वक हल किए गए वादे को वापस करेगा।

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