जावास्क्रिप्ट वादे - अस्वीकार बनाम फेंक


384

मैंने इस विषय पर कई लेख पढ़े हैं, लेकिन यह मेरे लिए अभी भी स्पष्ट नहीं है कि क्या Promise.rejectत्रुटि को फेंकने के बीच अंतर है । उदाहरण के लिए,

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

return asyncIsPermitted()
    .then(function(result) {
        if (result === true) {
            return true;
        }
        else {
            return Promise.reject(new PermissionDenied());
        }
    });

फेंक का उपयोग करना

return asyncIsPermitted()
    .then(function(result) {
        if (result === true) {
            return true;
        }
        else {
            throw new PermissionDenied();
        }
    });

मेरी प्राथमिकता throwसिर्फ इसलिए है क्योंकि यह छोटा है, लेकिन अगर कोई एक दूसरे के ऊपर कोई फायदा है तो सोच रहा था।


9
दोनों विधियाँ एक समान प्रतिक्रिया उत्पन्न करती हैं। .then()हैंडलर फेंका अपवाद पकड़ता है और एक को अस्वीकार कर दिया वादा में यह स्वचालित रूप से बदल जाता है। चूँकि मैंने पढ़ा है कि फेंके गए अपवादों को निष्पादित करने के लिए विशेष रूप से तेज़ नहीं हैं, इसलिए मुझे लगता है कि अस्वीकार किए गए वादे को पूरा करने के लिए निष्पादित करने के लिए थोड़ा तेज़ हो सकता है, लेकिन आपको कई आधुनिक ब्राउज़रों में एक परीक्षण तैयार करना होगा यदि यह जानना महत्वपूर्ण था। मैं व्यक्तिगत रूप से उपयोग करता throwहूं क्योंकि मुझे पठनीयता पसंद है।
२०

@webduvet वादे के साथ नहीं - वे फेंक के साथ काम करने के लिए डिज़ाइन किए गए हैं।
joews

15
एक नकारात्मक पक्ष throwयह है कि अगर यह एक अतुल्यकालिक कॉलबैक, जैसे कि सेटटाइमआउट के भीतर से फेंका गया था, तो खारिज किए गए वादे का परिणाम नहीं होगा। jsfiddle.net/m07van33 @Blondie आपका उत्तर सही था।
केविन बी

@joews इसका मतलब यह अच्छा नहीं है;)
webduvet

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

जवाबों:


344

एक बनाम दूसरे का उपयोग करने का कोई फायदा नहीं है, लेकिन, एक विशिष्ट मामला है जहां throwकाम नहीं करेगा। हालांकि, उन मामलों को तय किया जा सकता है।

किसी भी समय आप एक वादा कॉलबैक के अंदर हैं, आप उपयोग कर सकते हैं throw। हालाँकि, यदि आप किसी अन्य अतुल्यकालिक कॉलबैक में हैं, तो आपको इसका उपयोग करना चाहिए reject

उदाहरण के लिए, यह पकड़ को ट्रिगर नहीं करेगा:

new Promise(function() {
  setTimeout(function() {
    throw 'or nah';
    // return Promise.reject('or nah'); also won't work
  }, 1000);
}).catch(function(e) {
  console.log(e); // doesn't happen
});

इसके बजाय आप एक अनसुलझे वादे और एक अनकहे अपवाद के साथ छोड़ दिए जाते हैं। यह एक ऐसा मामला है जहां आप इसके बजाय उपयोग करना चाहते हैं reject। हालाँकि, आप इसे दो तरीकों से ठीक कर सकते हैं।

  1. टाइमआउट के अंदर मूल वादा के अस्वीकार समारोह का उपयोग करके:

new Promise(function(resolve, reject) {
  setTimeout(function() {
    reject('or nah');
  }, 1000);
}).catch(function(e) {
  console.log(e); // works!
});

  1. टाइमआउट का वादा करके:

function timeout(duration) { // Thanks joews
  return new Promise(function(resolve) {
    setTimeout(resolve, duration);
  });
}

timeout(1000).then(function() {
  throw 'worky!';
  // return Promise.reject('worky'); also works
}).catch(function(e) {
  console.log(e); // 'worky!'
});


54
इस बात का उल्लेख करते हुए कि गैर-प्रांप्ट किए गए async कॉलबैक के अंदर की जगहें जिनका आप उपयोग नहीं कर सकते हैं throw error, आप वह भी उपयोग नहीं कर सकते return Promise.reject(err)जो ओपी हमसे तुलना करने के लिए कह रहा था। यह मूल रूप से इसलिए है कि आपको वादों के अंदर एसिंक्स कॉलबैक नहीं डालना चाहिए। सब कुछ जो कि Async है, उसे वादा करें और फिर आपके पास ये प्रतिबंध नहीं हैं।
23

9
"हालांकि, यदि आप किसी अन्य प्रकार के कॉलबैक में हैं" वास्तव में "होना चाहिए" हालांकि, यदि आप किसी अन्य प्रकार के एसिंक्रोनसबैक में हैं "। कॉलबैक सिंक्रोनस हो सकता है (उदाहरण के साथ Array#forEach) और उन लोगों के साथ, उनके अंदर फेंकना काम करेगा।
फेलिक्स सपरेली

2
@KevinB इन पंक्तियों को पढ़ते हुए "एक विशिष्ट मामला है जहां फेंक काम नहीं करेगा।" और "जब भी आप एक वादा कॉलबैक के अंदर हैं, तो आप फेंक उपयोग कर सकते हैं। हालांकि, अगर आप किसी भी अन्य अतुल्यकालिक कॉलबैक में हैं, तो आप अस्वीकार उपयोग करना चाहिए।" मुझे लगता है कि उदाहरण के टुकड़े मामलों दिखाएगा जहां मिलता है throwकाम नहीं करेगा और इसके बजाय Promise.rejectएक बेहतर विकल्प है। हालाँकि स्निपेट्स उन दो विकल्पों में से किसी के साथ अप्रभावित हैं और आप जो भी चुनते हैं, उसी तरह का परिणाम देते हैं। क्या मैं कुछ भूल रहा हूँ?
अंशुल

2
हाँ। यदि आप एक setTimeout में फेंक उपयोग करते हैं, पकड़ बुलाया नहीं किया जाएगा। आप का उपयोग करना चाहिए rejectकि new Promise(fn)कॉलबैक के लिए पारित किया गया था ।
केविन बी

2
@ केविनबी साथ रहने के लिए धन्यवाद। ओपी द्वारा दिए गए उदाहरण में उल्लेख किया गया है कि वह विशेष रूप से तुलना करना चाहता था return Promise.reject()और throw। वह निर्माण rejectमें दिए गए कॉलबैक का उल्लेख नहीं करता है new Promise(function(resolve, reject))। इसलिए जब आपके दो स्निपेट सही तरीके से प्रदर्शित होते हैं, जब आपको रिज़ॉल्यूशन कॉलबैक का उपयोग करना चाहिए, तो ओपी का सवाल यह नहीं था।
अंशुल

200

एक अन्य महत्वपूर्ण तथ्य यह है कि एक बयान की तरह नियंत्रण प्रवाह को समाप्त reject() नहींreturn करता है। इसके विपरीत throwनियंत्रण प्रवाह को समाप्त करता है।

उदाहरण:

new Promise((resolve, reject) => {
  throw "err";
  console.log("NEVER REACHED");
})
.then(() => console.log("RESOLVED"))
.catch(() => console.log("REJECTED"));

बनाम

new Promise((resolve, reject) => {
  reject(); // resolve() behaves similarly
  console.log("ALWAYS REACHED"); // "REJECTED" will print AFTER this
})
.then(() => console.log("RESOLVED"))
.catch(() => console.log("REJECTED"));


50
खैर बात सही है लेकिन तुलना मुश्किल है। क्योंकि आम तौर पर आपको लिखित रूप से अपना अस्वीकृत वादा वापस करना चाहिए return reject(), इसलिए अगली पंक्ति नहीं चलेगी।
अज।

7
आप इसे क्यों लौटना चाहेंगे?
lukyer

31
इस मामले में, return reject()बस के लिए एक आशुलिपि है reject(); returnकि आप क्या चाहते हैं प्रवाह को समाप्त करना है। निष्पादक का रिटर्न मान (पास किया गया फ़ंक्शन new Promise) उपयोग नहीं किया जाता है, इसलिए यह सुरक्षित है।
फेलिक्स सपरेली

47

हां, सबसे बड़ा अंतर यह है कि अस्वीकार एक कॉलबैक फ़ंक्शन है जो वादे को अस्वीकार किए जाने के बाद किया जाता है, जबकि फेंक को अतुल्यकालिक रूप से उपयोग नहीं किया जा सकता है। यदि आपने अस्वीकार का उपयोग करना चुना है, तो आपका कोड सामान्य रूप से अतुल्यकालिक फैशन में चलता रहेगा जबकि थ्रो रिसोल्वर फ़ंक्शन को पूरा करने को प्राथमिकता देगा (यह फ़ंक्शन तुरंत चलेगा)।

एक उदाहरण मैंने देखा है कि मेरे लिए इस मुद्दे को स्पष्ट करने में मदद मिली कि आप अस्वीकार के साथ एक टाइमआउट फ़ंक्शन सेट कर सकते हैं, उदाहरण के लिए:

new Promise(_, reject) {
 setTimeout(reject, 3000);
});

इसके बाद के संस्करण सकता है फेंक के साथ लिखने के लिए संभव नहीं होगा।

आपके छोटे उदाहरण में अविभाज्य में अंतर है लेकिन अधिक जटिल अतुल्यकालिक अवधारणा से निपटने पर दोनों के बीच का अंतर कठोर हो सकता है।


1
यह एक मुख्य अवधारणा की तरह लगता है, लेकिन मैं इसे लिखित रूप में नहीं समझता। फिर भी वादा करने के लिए बहुत नया, मुझे लगता है।
डेविड स्पेक्टर

43

TLDR: एक फ़ंक्शन का उपयोग करना कठिन होता है जब यह कभी-कभी एक वादा वापस करता है और कभी-कभी एक अपवाद फेंकता है। जब कोई एसिंक्स फ़ंक्शन लिखता है, तो खारिज किए गए वादे को वापस करके विफलता का संकेत देना पसंद करता है

आपका विशिष्ट उदाहरण obfuscates उन दोनों के बीच कुछ महत्वपूर्ण अंतर:

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

नीचे की स्थिति पर विचार करें:

checkCredentials = () => {
    let idToken = localStorage.getItem('some token');
    if ( idToken ) {
      return fetch(`https://someValidateEndpoint`, {
        headers: {
          Authorization: `Bearer ${idToken}`
        }
      })
    } else {
      throw new Error('No Token Found In Local Storage')
    }
  }

यह एक विरोधी पैटर्न होगा क्योंकि आप तो दोनों async और समन्वयन त्रुटि मामलों की विशेष सहायता की आवश्यकता होगी। यह कुछ इस तरह लग सकता है:

try {
  function onFulfilled() { ... do the rest of your logic }
  function onRejected() { // handle async failure - like network timeout }
  checkCredentials(x).then(onFulfilled, onRejected);
} catch (e) {
  // Error('No Token Found In Local Storage')
  // handle synchronous failure
} 

अच्छा और यहाँ नहीं कहां है Promise.reject(उपलब्ध वैश्विक क्षेत्र में) बचाव के लिए आता और प्रभावी ढंग से से खुद को अलग करता है throw। रिफ्लेक्टर अब बन गया है:

checkCredentials = () => {
  let idToken = localStorage.getItem('some_token');
  if (!idToken) {
    return Promise.reject('No Token Found In Local Storage')
  }
  return fetch(`https://someValidateEndpoint`, {
    headers: {
      Authorization: `Bearer ${idToken}`
    }
  })
}

अब यह आपको catch()नेटवर्क विफलताओं के लिए सिर्फ एक का उपयोग करने देता है और टोकन की कमी के लिए तुल्यकालिक त्रुटि की जांच करता है:

checkCredentials()
      .catch((error) => if ( error == 'No Token' ) {
      // do no token modal
      } else if ( error === 400 ) {
      // do not authorized modal. etc.
      }

1
हालाँकि, Op का उदाहरण हमेशा एक वादा देता है। प्रश्न यह उल्लेख कर रहा है कि क्या आपको उपयोग करना चाहिए Promise.rejectया throwजब आप अस्वीकार किए गए वादे को वापस करना चाहते हैं (एक वादा जो अगले पर कूद जाएगा .catch())।
मार्कोस परेरा

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

1
बहुत बढ़िया जवाब लेकिन मैं यहाँ एक दोष लगता है - इस पद्धति मान सभी त्रुटियों को एक Promise.reject वापस लौट कर नियंत्रित किया जाता है - क्या सभी अप्रत्याशित त्रुटियों कि बस checkCredentials से फेंका जा सकता है के साथ होता है ()?
चिनॉप

1
हाँ, आप सही हैं @chenop - उन अप्रत्याशित त्रुटियों को पकड़ने के लिए जिन्हें आपको कोशिश करने के लिए लपेटना होगा / अभी भी पकड़ना होगा
maxwell

मुझे समझ नहीं आता @ मैक्सवेल का मामला। क्या आप ऐसा नहीं कर सकते हैं इसलिए आप इसे करते हैं checkCredentials(x).then(onFulfilled).catch(e) {}, और आपके पास catchअस्वीकृति मामले और फेंके गए त्रुटि मामले दोनों हैं?
बेन व्हीलर

5

एक उदाहरण आज़माने के लिए। फेंक के बजाय अस्वीकार का उपयोग करने के लिए बस परिवर्तन।

const isVersionThrow = true

class TestClass {
  async testFunction () {
    if (isVersionThrow) {
      console.log('Throw version')
      throw new Error('Fail!')
    } else {
      console.log('Reject version')
      return new Promise((resolve, reject) => {
        reject(new Error('Fail!'))
      })
    }
  }
}

const test = async () => {
  const test = new TestClass()
  try {
    var response = await test.testFunction()
    return response 
  } catch (error) {
    console.log('ERROR RETURNED')
    throw error 
  }  
}

test()
.then(result => {
  console.log('result: ' + result)
})
.catch(error => {
  console.log('error: ' + error)
})

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