क्या केवल त्रुटि मामलों के लिए एक वादा खारिज कर रहा है?


25

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


यदि प्रमाणीकरण विफल हो गया है, तो आपको rejectगलत नहीं लौटना चाहिए , लेकिन यदि आप मान होने की उम्मीद कर रहे हैं Bool, तो आप सफल थे और आपको मूल्य की परवाह किए बिना बूल के साथ हल करना चाहिए। मूल्य मूल्यों के लिए प्रॉक्सी के प्रकार हैं - वे लौटे मूल्य को संग्रहीत करते हैं, इसलिए केवल अगर मूल्य प्राप्त नहीं किया जा सकता है तो आपको चाहिए reject। नहीं तो आपको चाहिए resolve

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

1
मैं कहूंगा कि समाधान का अर्थ है प्रतिक्रिया का उपयोग करना और अपने आवेदन को जारी रखना , जबकि अस्वीकार करने का अर्थ है वर्तमान ऑपरेशन को रद्द करना (और संभवतः फिर से प्रयास करें या कुछ और करें)।

4
इसके बारे में सोचने का एक और तरीका है - अगर यह एक तुल्यकालिक विधि कॉल थी, तो क्या आप नियमित प्रमाणीकरण विफलता (खराब उपयोगकर्ता नाम / पासवर्ड) को वापस लौटने falseया अपवाद के रूप में मानेंगे?
१५:१६ को १५:१६

2
लायें एपीआई इस का एक अच्छा उदाहरण है। thenजब सर्वर प्रतिक्रिया देता है तो यह हमेशा ट्रिगर होता है - भले ही त्रुटि कोड वापस आ जाए - और आपको इसकी जांच करनी चाहिए response.okcatchहैंडलर के लिए ही शुरू हो रहा है अप्रत्याशित त्रुटियों।
कोडिंगइंट्रीग्यू

जवाबों:


22

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

एक Promiseको अस्वीकार करना एक ही एक अपवाद को बढ़ाने वाला है। सभी अवांछित परिणाम असाधारण नहीं हैं , त्रुटियों का परिणाम है । आप दोनों तरह से अपने मामले पर बहस कर सकते हैं:

  1. विफल प्रमाणीकरण करना चाहिए , क्योंकि फोन करने वाले एक उम्मीद कर रही है बदले में वस्तु, और कुछ और इस प्रवाह के लिए एक अपवाद है।rejectPromiseUser

  2. विफल प्रमाणीकरण के resolveलिए Promise, भले ही null, गलत क्रेडेंशियल्स प्रदान करने के लिए वास्तव में एक असाधारण मामला नहीं है, और कॉल करने वाले को प्रवाह के परिणाम की उम्मीद नहीं करनी चाहिए User

ध्यान दें कि मैं कॉलर की ओर से समस्या को देख रहा हूं । जानकारी के प्रवाह में, क्या कॉलर को अपने कार्यों के परिणाम की उम्मीद है User(और कुछ भी त्रुटि है), या क्या यह विशेष कॉलर के लिए अन्य परिणामों को संभालने के लिए समझ में आता है?

बहु-स्तरित प्रणाली में, उत्तर बदल सकता है क्योंकि डेटा परतों के माध्यम से बहता है। उदाहरण के लिए:

  • HTTP लेयर कहती है RESOLVE! अनुरोध भेजा गया था, सॉकेट साफ बंद हो गया, और सर्वर ने एक वैध प्रतिक्रिया उत्सर्जित की। लायें एपीआई करता है।
  • प्रोटोकॉल लेयर फिर कहता है REJECT! प्रतिक्रिया में स्थिति कोड 401 था, जो HTTP के लिए ठीक है, लेकिन प्रोटोकॉल के लिए नहीं!
  • ऑथेंटिकेशन लेयर कहती है NO, RESOLVE! यह त्रुटि को पकड़ता है, क्योंकि 401 एक गलत पासवर्ड के लिए अपेक्षित स्थिति है, और एक nullउपयोगकर्ता को हल करता है ।
  • इंटरफ़ेस नियंत्रक का कहना है कि कोई भी, अस्वीकार! स्क्रीन पर दिख रहा मॉडल एक उपयोगकर्ता नाम और एक अवतार की उम्मीद कर रहा था, और उस जानकारी के अलावा कुछ भी इस बिंदु पर एक त्रुटि है।

यह 4-बिंदु उदाहरण स्पष्ट रूप से जटिल है, लेकिन यह 2 बिंदुओं को दिखाता है:

  1. कुछ अपवाद है / अस्वीकृति या नहीं आसपास के प्रवाह और उम्मीदों पर निर्भर करता है
  2. आपके कार्यक्रम की अलग-अलग परतें समान परिणाम को अलग-अलग मान सकती हैं, क्योंकि वे प्रवाह के अलग-अलग चरणों में स्थित हैं

तो फिर, कोई मुश्किल जवाब नहीं। सोचने और डिजाइन करने का समय!


6

इसलिए वादों की एक अच्छी संपत्ति है कि वे जेएस को कार्यात्मक भाषाओं से लाते हैं, जो यह है कि वे वास्तव में इस Eitherप्रकार के निर्माता को लागू करते हैं जो दो अन्य प्रकारों, प्रकारों Leftऔर प्रकारों को एक साथ जोड़ते हैं Right, तर्क को या तो एक शाखा या दूसरे को लेने के लिए मजबूर करते हैं डाली।

data Either x y = Left x | Right y

अब आप वास्तव में देख रहे हैं कि बाईं ओर का प्रकार वादों के लिए अस्पष्ट है; आप किसी भी चीज़ को अस्वीकार कर सकते हैं। यह सच है क्योंकि जेएस कमजोर रूप से टाइप किया गया है, लेकिन यदि आप रक्षात्मक रूप से प्रोग्रामिंग कर रहे हैं तो आप सतर्क रहना चाहते हैं।

कारण यह है कि जेएस throwवादा-संभाल कोड से बयान लेगा और इसे उसी के Leftपक्ष में बंडल करेगा । जेएस में तकनीकी रूप से आप throwसब कुछ कर सकते हैं, जिसमें सही / गलत या एक स्ट्रिंग या एक संख्या शामिल है: लेकिन जावास्क्रिप्ट कोड भी बिना चीजों को फेंकता हैthrow (जब आप ऐसा करते हैं जैसे नल पर संपत्तियों तक पहुंचने की कोशिश कर रहे हैं) और इसके लिए एक व्यवस्थित एपीआई है ( Errorऑब्जेक्ट) । इसलिए जब आप पकड़ने के लिए चारों ओर हो जाते हैं, तो आमतौर पर यह अनुमान लगाने में सक्षम होना अच्छा होता है कि वे त्रुटियां हैं Error। और चूंकि rejectवादे के लिए उपरोक्त किसी भी बग से किसी भी त्रुटि में ढेर हो जाएगा, आप आम तौर पर केवल throwअन्य त्रुटियां करना चाहते हैं, ताकि आपके catchकथन में एक सरल, सुसंगत तर्क हो।

इसलिए यद्यपि आप अपने में एक शर्त लगा सकते हैंcatch और गलत त्रुटियों की तलाश कर सकते हैं, जिस मामले में सत्य मामला तुच्छ है,

Either (Either Error ()) ()

आप शायद तर्क संरचना को पसंद करेंगे, कम से कम जो प्रामाणिक लेखक से तुरंत निकलता है, एक सरल बूलियन के लिए:

Either Error Bool

वास्तव में प्रमाणीकरण तर्क का अगला स्तर संभवतः Userप्रमाणित उपयोगकर्ता वाले किसी प्रकार की वस्तु को वापस करना है , ताकि यह बन जाए:

Either Error (Maybe User)

और यह कम या ज्यादा है जो मैं उम्मीद करूंगा: nullउस मामले में वापस लौटें जहां उपयोगकर्ता परिभाषित नहीं है, अन्यथा वापस लौटें {user_id: <number>, permission_to_launch_missiles: <boolean>}। मैं उम्मीद थी कि लॉग इन नहीं किया जा रहा के सामान्य मामले salvageable है, उदाहरण के लिए अगर हम "नए ग्राहकों के लिए डेमो" मोड में किसी प्रकार का में हैं, और कीड़े जहाँ मैं गलती से बुलाया के साथ में मिश्रित नहीं होना चाहिए object.doStuff()जब object.doStuffथा undefined

अब उस के साथ कहा, आप क्या करना चाहते हो सकता है एक अपवाद को परिभाषित NotLoggedInया PermissionErrorजो से प्राप्त होता है Error। फिर उन चीजों में जिन्हें वास्तव में आपको लिखने की आवश्यकता है:

function launchMissiles() {
    function actuallyLaunchThem() {
        // stub
    }
    return getAuth().then(auth => {
        if (auth === null) {
            throw new PermissionError('Cannot launch missiles without permission, cannot have permission if not logged in.');
        } else if (auth.permission_to_launch_missiles) {
            return actuallyLaunchThem();
        } else {
            throw new PermissionError(`User ${auth.user_id} does not have permission to launch the missiles.`);
        }
    });
}

3

त्रुटियाँ

त्रुटियों के बारे में बात करते हैं।

त्रुटियों के दो प्रकार हैं:

  • अपेक्षित त्रुटियां
  • अप्रत्याशित त्रुटियां
  • एक-के-बाद एक त्रुटियां

अपेक्षित त्रुटियाँ

अपेक्षित त्रुटियां ऐसी स्थिति हैं जहां गलत काम होता है लेकिन आप जानते हैं कि यह हो सकता है, इसलिए आप इससे निपटते हैं।

ये उपयोगकर्ता इनपुट या सर्वर अनुरोध जैसी चीजें हैं। आप जानते हैं कि उपयोगकर्ता कोई गलती कर सकता है या सर्वर डाउन हो सकता है, इसलिए आप यह सुनिश्चित करने के लिए कुछ चेकिंग कोड लिखते हैं कि प्रोग्राम फिर से इनपुट मांगता है, या एक संदेश प्रदर्शित करता है, या जो भी अन्य व्यवहार उचित है।

हाथ लगने पर ये ठीक हो जाते हैं। यदि उन्हें छोड़ दिया जाता है, तो वे अप्रत्याशित त्रुटियां हो जाती हैं।

अनपेक्षित त्रुटियां

अनपेक्षित त्रुटियां (बग) वे अवस्थाएं हैं जहां गलत काम होता है क्योंकि कोड गलत है। आप जानते हैं कि वे अंततः हो जाएंगे, लेकिन यह जानने का कोई तरीका नहीं है कि उनके साथ कहां या कैसे व्यवहार करें, क्योंकि परिभाषा के अनुसार, वे अप्रत्याशित हैं।

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

try..catch

की बात करते हैं try..catch

जावास्क्रिप्ट में, throwआमतौर पर इस्तेमाल नहीं किया जाता है। यदि आप कोड में उदाहरणों के लिए चारों ओर देखते हैं तो वे कुछ और दूर के बीच होने वाले हैं, और आमतौर पर की तर्ज पर संरचित होते हैं

function example(param) {
  if (!Array.isArray(param) {
    throw new TypeError('"param" should be an array!');
  }
  ...
}

इस वजह से, try..catchब्लॉक या तो नियंत्रण प्रवाह के लिए सामान्य नहीं हैं। आमतौर पर अपेक्षित त्रुटियों से बचने के लिए कॉलिंग विधियों से पहले कुछ चेक जोड़ना बहुत आसान है।

जावास्क्रिप्ट वातावरण के रूप में अच्छी तरह से माफ कर रहे हैं, इसलिए अप्रत्याशित त्रुटियों अक्सर के रूप में अच्छी तरह से छोड़ दिया जाता है।

try..catchअसामान्य होने की जरूरत नहीं है। कुछ अच्छे उपयोग के मामले हैं, जो जावा और सी # जैसी भाषाओं में अधिक सामान्य हैं। जावा और C # में टाइप किए गए catchनिर्माणों का लाभ है , ताकि आप अपेक्षित और अप्रत्याशित त्रुटियों के बीच अंतर कर सकें:

सी # :
try
{
  var example = DoSomething();
}
catch (ExpectedException e)
{
  DoSomethingElse(e);
}

यह उदाहरण अन्य अप्रत्याशित अपवादों को प्रवाहित करने की अनुमति देता है और कहीं और संभाला जा सकता है (जैसे कि लॉग किया जा रहा है और कार्यक्रम को बंद कर रहा है)।

जावास्क्रिप्ट में, इस निर्माण को इसके माध्यम से दोहराया जा सकता है:

try {
  let example = doSomething();
} catch (e) {
  if (e instanceOf ExpectedError) {
    DoSomethingElse(e);
  } else {
    throw e;
  }
}

उतना सुरुचिपूर्ण नहीं, जो इस कारण का हिस्सा है कि यह असामान्य क्यों है।

कार्य

कार्यों के बारे में बात करते हैं।

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

उदाहरण के लिए, authenticate()किसी उपयोगकर्ता को प्रमाणित कर सकता है।

इस रूप में लिखा जा सकता है:

const user = authenticate();
if (user == null) {
  // keep doing stuff
} else {
  // handle expected error
}

वैकल्पिक रूप से इसे इस प्रकार लिखा जा सकता है:

try {
  const user = authenticate();
  // keep doing stuff
} catch (e) {
  if (e instanceOf AuthenticationError) {
    // handle expected error
  } else {
    throw e;
  }
}

दोनों स्वीकार्य हैं।

वादे

वादों की बात करते हैं।

वादे एक अतुल्यकालिक रूप हैं try..catch। कॉल करना new Promiseया Promise.resolveअपना tryकोड शुरू करना । आपको कॉल करना throwया कोड Promise.rejectभेजना catch

Promise.resolve(value)   // try
  .then(doSomething)     // try
  .then(doSomethingElse) // try
  .catch(handleError)    // catch

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

authenticate()
  .then((user) => {
    if (user == null) {
      // keep doing stuff
    } else {
      // handle expected error
    }
  });

वैकल्पिक रूप से इसे इस प्रकार लिखा जा सकता है:

authenticate()
  .then((user) => {
    // keep doing stuff
  })
  .catch((e) => {
    if (e instanceOf AuthenticationError) {
      // handle expected error
    } else {
      throw e;
    }
  });

दोनों स्वीकार्य हैं।

घोंसला करने की क्रिया

बात करते हैं नेस्टिंग की।

try..catchनेस्ट किया जा सकता है। आपके authenticate()तरीके में आंतरिक रूप से एक try..catchब्लॉक हो सकता है जैसे:

try {
  const credentials = requestCredentialsFromUser();
  const user = getUserFromServer(credentials);
} catch (e) {
  if (e instanceOf CredentialsError) {
    // handle failure to request credentials
  } else if (e instanceOf ServerError) {
    // handle failure to get data from server
  } else {
    throw e; // no idea what happened
  }
}

इसी तरह वादे भी किए जा सकते हैं। आपके async authenticate()विधि आंतरिक रूप से वादों का उपयोग कर सकती है:

requestCredentialsFromUser()
  .then(getUserFromServer)
  .catch((e) => {
    if (e instanceOf CredentialsError) {
      // handle failure to request credentials
    } else if (e instanceOf ServerError) {
      // handle failure to get data from server
    } else {
      throw e; // no idea what happened
    }
  });

तो जवाब क्या है?

ठीक है, मुझे लगता है कि मेरे लिए वास्तव में इस सवाल का जवाब देने का समय है:

क्या प्रमाणीकरण में विफलता के कारण कुछ ऐसा माना जाता है जिसे आप के लिए वादा अस्वीकार कर देंगे?

सबसे सरल उत्तर जो मैं दे सकता हूं, वह यह है कि आपको कहीं भी एक वादे को अस्वीकार कर देना चाहिए अन्यथा आप throwएक अपवाद चाहते हैं यदि यह समकालिक कोड हो।

अगर आपके बयानों ifमें कुछ जाँच होने से आपका नियंत्रण प्रवाह सरल है then, तो किसी वादे को अस्वीकार करने की आवश्यकता नहीं है।

यदि किसी वादे को अस्वीकार करने और फिर आपकी त्रुटि हैंडलिंग कोड में त्रुटियों के प्रकार की जांच करके आपका नियंत्रण प्रवाह सरल है, तो इसके बजाय ऐसा करें।


0

मैंने jQuery UI डायलॉग बॉक्स की "रद्द" कार्रवाई का प्रतिनिधित्व करने के लिए एक वादे की "अस्वीकार" शाखा का उपयोग किया है। यह "रिज़ॉल्यूशन" शाखा का उपयोग करने की तुलना में अधिक स्वाभाविक लगता था, कम से कम नहीं क्योंकि संवाद बॉक्स पर अक्सर कई "करीब" विकल्प होते हैं।


मुझे पता है कि अधिकांश शुद्धतावादी आपसे असहमत होंगे।

0

एक वादा संभालना " कम " स्थिति की तरह अधिक या कम है । यह आपके ऊपर है कि क्या आप प्रमाणीकरण को विफल करने के लिए "हल" या "अस्वीकार" करना चाहते हैं।


1
वादा एक अतुल्यकालिक है try..catch, नहीं if
zzzzBov

@zzzBox तो उस तर्क से, आपको एक एसिंक्रोनस के रूप में एक प्रोमिस का उपयोग करना चाहिए try...catchऔर बस यह कहना चाहिए कि यदि आप पूरा करने और परिणाम प्राप्त करने में सक्षम थे, तो आपको प्राप्त किए गए मूल्य की परवाह किए बिना हल करना चाहिए, अन्यथा आपको अस्वीकार करना चाहिए?

@somethinghere, नहीं, आपने मेरा तर्क गलत समझा है। try { if (!doSomething()) throw whatever; doSomethingElse() } catch { ... }पूरी तरह से ठीक है, लेकिन निर्माण जो Promiseप्रतिनिधित्व करता है वह try..catchहिस्सा है, भाग नहीं if
zzzzBov

@zzzzBov मुझे लगता है कि सभी निष्पक्षता में :) मुझे सादृश्य पसंद है। लेकिन मेरा तर्क बस इतना है कि अगर doSomething()विफल रहता है, तो यह फेंक देगा, लेकिन अगर यह आपके लिए आवश्यक मूल्य नहीं हो सकता है (आपके ifऊपर थोड़ा भ्रमित है क्योंकि यह आपके विचार का हिस्सा नहीं है :))। आपको केवल तभी अस्वीकार करना चाहिए अगर फेंकने का कोई कारण है (सादृश्य में), इसलिए यदि परीक्षण विफल हो गया। यदि परीक्षण सफल हुआ तो आपको हमेशा हल करना चाहिए, भले ही इसका मूल्य सकारात्मक हो, सही है?

@ शेष, मैंने एक उत्तर लिखने का फैसला किया है (यह मानते हुए कि यह काफी लंबे समय तक खुला रहता है), क्योंकि मेरे विचार व्यक्त करने के लिए टिप्पणियां पर्याप्त नहीं हैं।
zzzzBov
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.