मैं एक Promise.catch हैंडलर के अंदर क्यों नहीं फेंक सकता हूं?


127

मैं सिर्फ Errorकैचबैक कॉल के अंदर क्यों नहीं फेंक सकता और इस प्रक्रिया को त्रुटि को संभालने देता हूं जैसे कि यह किसी अन्य दायरे में था?

अगर मैं कुछ नहीं करता तो उसका console.log(err)प्रिंट आउट नहीं निकलता और जो कुछ हुआ उसके बारे में मुझे कुछ नहीं पता। प्रक्रिया अभी समाप्त होती है ...

उदाहरण:

function do1() {
    return new Promise(function(resolve, reject) {
        throw new Error('do1');
        setTimeout(resolve, 1000)
    });
}

function do2() {
    return new Promise(function(resolve, reject) {
        setTimeout(function() {
            reject(new Error('do2'));
        }, 1000)
    });
}

do1().then(do2).catch(function(err) {
    //console.log(err.stack); // This is the only way to see the stack
    throw err; // This does nothing
});

यदि कॉलबैक मुख्य धागे में निष्पादित किया Errorजाता है, तो ब्लैक होल क्यों निगल जाता है?


11
यह एक ब्लैक होल द्वारा निगल नहीं लिया जाता है। यह उस वादे को खारिज करता है जो .catch(…)रिटर्न करता है ।
बेर्गी


इसके बजाय .catch((e) => { throw new Error() }), लिखें .catch((e) => { return Promise.reject(new Error()) })या बस.catch((e) => Promise.reject(new Error()))
chharvey

1
@ अपनी टिप्पणी में सभी कोड स्निपेट्स बिल्कुल समान व्यवहार रखते हैं, सिवाय इसके कि प्रारंभिक एक स्पष्ट रूप से सबसे स्पष्ट है।
Сергей Гринько

जवाबों:


157

जैसा कि अन्य लोगों ने समझाया है, "ब्लैक होल" इसलिए है क्योंकि अंदर फेंकना .catchएक अस्वीकार किए गए वादे के साथ श्रृंखला को जारी रखता है, और आपके पास कोई और कैच नहीं है, जिसके कारण एक अव्यवस्थित श्रृंखला होती है, जो त्रुटियों (बुरी!) को निगल जाती है

क्या हो रहा है यह देखने के लिए एक और पकड़ जोड़ें:

do1().then(do2).catch(function(err) {
    //console.log(err.stack); // This is the only way to see the stack
    throw err; // Where does this go?
}).catch(function(err) {
    console.log(err.stack); // It goes here!
});

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

छल

वेब कंसोल में त्रुटि दिखाने के लिए, जैसा कि आपने मूल रूप से इरादा किया था, मैं इस ट्रिक का उपयोग करता हूं:

.catch(function(err) { setTimeout(function() { throw err; }); });

यहां तक ​​कि लाइन नंबर भी जीवित रहते हैं, इसलिए वेब कंसोल में लिंक मुझे सीधे फ़ाइल और लाइन पर ले जाता है जहां (मूल) त्रुटि हुई थी।

यह काम क्यों करता है

एक वादा पूर्ति या अस्वीकृति हैंडलर के रूप में कहे जाने वाले फ़ंक्शन में कोई भी अपवाद स्वचालित रूप से उस वादे की अस्वीकृति में परिवर्तित हो जाता है जिसे आप वापस करने वाले हैं। आपके फ़ंक्शन को कॉल करने वाला वादा कोड इस बात का ध्यान रखता है।

दूसरी ओर setTimeout द्वारा बुलाया गया एक फ़ंक्शन, हमेशा जावास्क्रिप्ट स्थिर स्थिति से चलता है, अर्थात यह जावास्क्रिप्ट इवेंट लूप में एक नए चक्र में चलता है। अपवाद कुछ भी नहीं पकड़ा है, और यह वेब कंसोल के लिए बनाते हैं। चूंकि errमूल स्टैक, फ़ाइल और लाइन नंबर सहित त्रुटि के बारे में सभी जानकारी रखती है, यह अभी भी सही ढंग से रिपोर्ट की जाती है।


3
जिब, यह एक दिलचस्प चाल है, क्या आप मुझे यह समझने में मदद कर सकते हैं कि क्यों काम करता है?
ब्रायन कीथ

8
उस ट्रिक के बारे में: आप फेंक रहे हैं क्योंकि आप लॉग इन करना चाहते हैं, इसलिए सिर्फ सीधे लॉग ही क्यों न करें? यह ट्रिक 'बेतरतीब' समय पर एक अनजाने त्रुटि को फेंक देगी .... लेकिन अपवादों का पूरा विचार (और जिस तरह से वादे उनसे निपटते हैं) यह त्रुटि को पकड़ने और उससे निपटने के लिए कॉलर की जिम्मेदारी है। यह कोड त्रुटियों से निपटने के लिए कॉलर के लिए प्रभावी रूप से असंभव बना देता है। क्यों न आप इसे अपने लिए संभाले। function logErrors(e){console.error(e)}फिर इसका उपयोग करें do1().then(do2).catch(logErrors)। उत्तर स्वयं महान btw, +1
Stijn de Witt

3
@ जीब मैं एक एडब्ल्यूएस लंबा लिख ​​रहा हूं जिसमें इस मामले में कमोबेश कई वादे जुड़े हुए हैं। त्रुटि के मामले में एडब्ल्यूएस अलार्म और नोटिफिकेशन का फायदा उठाने के लिए मुझे लैम्बडा क्रैश को एक एरर फेंकने की आवश्यकता है (मुझे लगता है)। क्या ट्रिक इसे प्राप्त करने का एकमात्र तरीका है?
मस्क्यूगो

2
@StijndeWitt मेरे मामले में, मैं window.onerrorइवेंट हैंडलर में अपने सर्वर पर त्रुटि विवरण भेजने की कोशिश कर रहा था । केवल setTimeoutट्रिक करने से ही ऐसा किया जा सकता है। अन्यथा window.onerrorवादे के बारे में एक बात कभी नहीं सुनी जाएगी वादा में।
hudidit 15

1
@hudidit फिर भी, यह घरघराहट है console.logया postErrorToServer, आप बस वही कर सकते हैं जो करने की आवश्यकता है। ऐसा कोई कारण नहीं है कि जो भी कोड है, window.onerrorउसे एक अलग फ़ंक्शन में शामिल नहीं किया जा सकता है और 2 स्थानों से बुलाया जा सकता है। यह शायद तब setTimeoutरेखा से भी छोटा है ।
स्टिजन डे विट

46

यहाँ समझने के लिए महत्वपूर्ण बातें

  1. दोनों thenऔर catchकार्य नए वादे वस्तुओं को वापस करते हैं।

  2. या तो फेंकना या स्पष्ट रूप से अस्वीकार करना, वर्तमान वादे को अस्वीकार किए गए राज्य में ले जाएगा।

  3. चूंकि thenऔर catchनए वादे वस्तुओं को वापस करते हैं, उन्हें जंजीर बनाया जा सकता है।

  4. यदि आप एक वादा हैंडलर ( thenया catch) के अंदर फेंकते हैं या अस्वीकार करते हैं , तो इसे अगले अस्वीकरण हैंडलर को चैनिंग मार्ग से संभाला जाएगा।

  5. जैसा कि jfriend00 ने उल्लेख किया है, thenऔर catchहैंडलर को समान रूप से निष्पादित नहीं किया जाता है। जब एक हैंडलर फेंकता है, तो यह तुरंत समाप्त हो जाएगा। तो, स्टैक अनफॉलो हो जाएगा और अपवाद खो जाएगा। यही कारण है कि एक अपवाद को फेंकना वर्तमान वादे को अस्वीकार करता है।


आपके मामले में, आप do1किसी Errorवस्तु को फेंककर अंदर अस्वीकार कर रहे हैं । अब, वर्तमान वादा अस्वीकृत स्थिति में होगा और नियंत्रण अगले हैंडलर को स्थानांतरित कर दिया जाएगा, जो thenहमारे मामले में है।

चूंकि thenहैंडलर के पास अस्वीकृति हैंडलर नहीं है, इसलिए do2इसे बिल्कुल भी निष्पादित नहीं किया जाएगा। आप console.logइसके अंदर का उपयोग करके इसकी पुष्टि कर सकते हैं । चूंकि वर्तमान वादे में अस्वीकृति हैंडलर नहीं है, इसलिए इसे पिछले वादे से अस्वीकृति मूल्य के साथ भी खारिज कर दिया जाएगा और नियंत्रण अगले हैंडलर को स्थानांतरित कर दिया जाएगा catch

जैसा catchकि एक अस्वीकृति हैंडलर है, जब आप इसके console.log(err.stack);अंदर करते हैं, तो आप त्रुटि स्टैक ट्रेस देखने में सक्षम होते हैं। अब, आप Errorइसमें से एक वस्तु फेंक रहे हैं, इसलिए द्वारा दिया गया वादा catchभी अस्वीकार की स्थिति में होगा।

चूंकि आपने किसी भी अस्वीकृति हैंडलर को संलग्न नहीं किया है catch, इसलिए आप अस्वीकृति का निरीक्षण नहीं कर सकते हैं।


आप श्रृंखला को विभाजित कर सकते हैं और इस तरह से बेहतर समझ सकते हैं

var promise = do1().then(do2);

var promise1 = promise.catch(function (err) {
    console.log("Promise", promise);
    throw err;
});

promise1.catch(function (err) {
    console.log("Promise1", promise1);
});

आपको जो आउटपुट मिलेगा वह कुछ इस तरह होगा

Promise Promise { <rejected> [Error: do1] }
Promise1 Promise { <rejected> [Error: do1] }

catchहैंडलर 1 के अंदर , आपको promiseअस्वीकृत के रूप में ऑब्जेक्ट का मूल्य मिल रहा है ।

उसी तरह, catchहैंडलर 1 द्वारा लौटाए गए वादे को भी उसी त्रुटि के साथ promiseखारिज कर दिया जाता है जिसके साथ खारिज कर दिया गया था और हम इसे दूसरे catchहैंडलर में देख रहे हैं ।


3
जोड़ने के लायक भी हो सकता है कि .then()हैंडलर async हैं (स्टैक अनलॉउंड होने से पहले वे निष्पादित किए जाते हैं) इसलिए उनके अंदर के अपवादों को अस्वीकार में बदलना होगा, अन्यथा उन्हें पकड़ने के लिए कोई अपवाद हैंडलर नहीं होगा।
jfriend00

7

मैंने setTimeout()ऊपर दी गई विधि की कोशिश की ...

.catch(function(err) { setTimeout(function() { throw err; }); });

अनायास, मैंने इसे पूरी तरह से अप्राप्य पाया। क्योंकि यह एक अतुल्यकालिक त्रुटि को फेंक रहा है, आप इसे एक try/catchबयान के अंदर नहीं लपेट सकते , क्योंकि catchसमय की त्रुटि को सुनने से वसीयत ने सुनना बंद कर दिया है।

मैं सिर्फ एक श्रोता का उपयोग करने के लिए वापस आया जो पूरी तरह से काम करता है और, क्योंकि यह है कि जावास्क्रिप्ट का उपयोग कैसे किया जाता है, अत्यधिक परीक्षण योग्य था।

return new Promise((resolve, reject) => {
    reject("err");
}).catch(err => {
    this.emit("uncaughtException", err);

    /* Throw so the promise is still rejected for testing */
    throw err;
});

3
जेस्ट में टाइमर मोक्स हैं जो इस स्थिति को संभालना चाहिए।
जोर्डनबटकर

2

युक्ति के अनुसार (देखें 3.III.d) :

घ। यदि कॉलिंग है तो एक अपवाद ई फेंकता है,
  ए। यदि रिज़ॉल्यूशन या रिजेक्टप्रोमाइज को कॉल किया गया है, तो इसे अनदेखा करें।
  ख। अन्यथा, कारण के रूप में ई के साथ वादा अस्वीकार करें।

इसका मतलब है कि यदि आप thenफ़ंक्शन में अपवाद फेंकते हैं , तो वह पकड़ा जाएगा और आपका वादा अस्वीकार कर दिया जाएगा। catchयहाँ कोई मतलब नहीं है, यह सिर्फ शॉर्टकट है.then(null, function() {})

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


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

1

मुझे पता है कि यह थोड़ा देर से है, लेकिन मैं इस धागे के पार आया, और कोई भी समाधान मेरे लिए लागू करना आसान नहीं था, इसलिए मैं अपने साथ आया:

मैंने थोड़ा सहायक फ़ंक्शन जोड़ा, जो एक वादा लौटाता है, जैसे:

function throw_promise_error (error) {
 return new Promise(function (resolve, reject){
  reject(error)
 })
}

फिर, यदि मेरे किसी वादे की श्रृंखला में मेरा कोई विशिष्ट स्थान है, जहाँ मैं एक त्रुटि फेंकना चाहता हूँ (और वादे को अस्वीकार करना), तो मैं अपनी निर्मित त्रुटि के साथ उपरोक्त फ़ंक्शन से लौटता हूँ, जैसे:

}).then(function (input) {
 if (input === null) {
  let err = {code: 400, reason: 'input provided is null'}
  return throw_promise_error(err)
 } else {
  return noterrorpromise...
 }
}).then(...).catch(function (error) {
 res.status(error.code).send(error.reason);
})

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

उम्मीद है कि यह मदद करता है, यह मेरी पहली stackoverflow जवाब है!


Promise.reject(error)इसके बजाय new Promise(function (resolve, reject){ reject(error) })(जिसे किसी भी तरह से वापसी विवरण की आवश्यकता होगी)
फंककोडैट

0

हां वादे त्रुटियों को निगलते हैं, और आप केवल उन्हें पकड़ सकते हैं .catch, जैसा कि अन्य उत्तरों में विस्तार से बताया गया है। यदि आप Node.js में हैं और सामान्य throwव्यवहार को पुन: उत्पन्न करना चाहते हैं, तो कंसोल और निकास प्रक्रिया के लिए स्टैक ट्रेस का मुद्रण, आप कर सकते हैं

...
  throw new Error('My error message');
})
.catch(function (err) {
  console.error(err.stack);
  process.exit(0);
});

1
नहीं, यह पर्याप्त नहीं है, जैसा कि आपको हर वादा श्रृंखला के अंत में रखना होगा। बल्कि पर हुक unhandledRejectionघटना
Bergi

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

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