Async / अस्वीकृति सिंटैक्स में अस्वीकार कैसे करें?


282

मैं एक वादा कैसे अस्वीकार कर सकता हूं जो एक async / प्रतीक्षा फ़ंक्शन द्वारा लौटाया गया है?

मूल रूप से

foo(id: string): Promise<A> {
  return new Promise((resolve, reject) => {
    someAsyncPromise().then((value)=>resolve(200)).catch((err)=>reject(400))
  });
}

अनुवाद में async / प्रतीक्षा करें

async foo(id: string): Promise<A> {
  try{
    await someAsyncPromise();
    return 200;
  } catch(error) {//here goes if someAsyncPromise() rejected}
    return 400; //this will result in a resolved promise.
  });
}

तो, मैं इस मामले में इस वादे को कैसे ठीक से खारिज कर सकता हूं?


20
Promiseकंस्ट्रक्टर एंटीपैटर्न से बचें ! यहां तक ​​कि पहले स्निपेट को भी लिखा जाना चाहिए थाfoo(id: string): Promise<A> { return someAsyncPromise().then(()=>{ return 200; }, ()=>{ throw 400; }); }
बरगी

10
मुझे लगता है कि इस प्रश्न में कोड को वैनिला JS में अनुवाद करना मददगार होगा, क्योंकि प्रश्न का टाइपस्क्रिप्ट से कोई लेना-देना नहीं है। अगर मैंने ऐसा किया है तो क्या उस संभावना को स्वीकार किया जाएगा?
जैकब फोर्ड

जवाबों:


328

आपका सबसे अच्छा शर्त throwएक Errorरैपिंग वैल्यू है, जिसके परिणामस्वरूप एक Errorरैपिंग वैल्यू के साथ अस्वीकृत वादा किया गया है:

} catch (error) {
    throw new Error(400);
}

आप केवल throwमान भी ले सकते हैं , लेकिन तब कोई स्टैक ट्रेस जानकारी नहीं है:

} catch (error) {
    throw 400;
}

वैकल्पिक रूप से, Errorमानों को लपेटकर अस्वीकार कर दिया गया वादा वापस करें , लेकिन यह मुहावरेदार नहीं है:

} catch (error) {
    return Promise.reject(new Error(400));
}

(या बस return Promise.reject(400);, लेकिन फिर, फिर कोई संदर्भ जानकारी नहीं है।)

(आपके मामले में, जैसा कि आप उपयोग कर रहे हैं TypeScriptऔर fooपुनः मूल्य है Promise<A>, आप उपयोग करेंगे return Promise.reject<A>(400 /*or error*/);)

एक async/ awaitस्थिति में, वह अंतिम शायद एक अर्थपूर्ण गलत मिलान का एक सा है, लेकिन यह काम करता है।

यदि आप एक फेंक देते हैं Error, जो सिंटैक्स के fooसाथ आपके परिणाम का उपभोग करने वाली किसी भी चीज़ के साथ अच्छा खेलता है await:

try {
    await foo();
} catch (error) {
    // Here, `error` would be an `Error` (with stack trace, etc.).
    // Whereas if you used `throw 400`, it would just be `400`.
}

12
और चूंकि एसिंक्स / वेट के बारे में है कि सिंटैक्स को सिंक सिंटैक्स में वापस ले जाना आईएमओ throwसे बेहतर है Promise.reject()। करना है या नहीं throw 400एक अलग सवाल है। ओपी में यह 400 को खारिज कर रहा है, और हम तर्क दे सकते हैं कि इसे Errorबदले में अस्वीकार करना चाहिए ।
यूनियनों

2
हां, हालाँकि, यदि आपकी कोड श्रंखला वास्तव में async / प्रतीक्षा का उपयोग कर रही है, तो आप ..... यहाँ टाइप करने के लिए कठिन हैं, मुझे उत्तर के रूप में डेमो दें
unional

1
क्या कोई कारण है कि आप कैच ब्लॉक में दी गई त्रुटि के विपरीत एक नई त्रुटि फेंकना चाहेंगे?
एड्रियन एम

1
@sebastian - मुझे नहीं पता कि आपका वहां क्या मतलब है। में asyncकाम करता है, कोई resolveया rejectकार्य करते हैं। नहीं है returnऔर throwहै, जो संकल्प को मुहावरेदार तरीके हैं और अस्वीकार asyncसमारोह का वादा।
टीजे क्राउडर

1
@ Jan-PhilipGehrcke - आप कर सकते हैं , लेकिन मैं कभी नहीं करता। यह एक उदाहरण newबना रहा है , यह स्पष्ट करता है। यह भी ध्यान दें कि यदि आपके पास Errorउपवर्ग ( class MyError extends Error) है, तो आप इसे नहीं छोड़ सकते ...
TJ Crowder

146

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

await foo().catch(error => console.log(error));

इस तरह आप try/catchसिंटैक्स से बच सकते हैं यदि आपको यह पसंद नहीं है।


1
इसलिए अगर मैं अपने asyncकार्य को अस्वीकार करना चाहता हूं , तो मैं अपवाद को फेंक देता हूं और फिर इसे अच्छी तरह से पकड़ता .catch()हूं जैसे कि मैं लौट आया Promise.rejectया बुलाया गया reject। मुझें यह पसंद है!
icl7126

7
मुझे समझ नहीं आता कि यह क्यों स्वीकार किया जाना चाहिए। न केवल स्वीकृत उत्तर क्लीनर है, बल्कि यह awaitएक दिनचर्या में सभी संभावित विफलताओं को भी संभालता है। जब तक प्रत्येक के लिए बहुत विशिष्ट मामलों की आवश्यकता awaitनहीं होती, मैं नहीं देखता कि आप उन्हें इस तरह क्यों पकड़ना चाहते हैं। बस मुझे विनम्र राय है।
edgaralienfoe

1
मेरे उपयोग के मामले के लिए @jablesauce, न केवल मुझे प्रत्येक awaitविफलता को अलग से पकड़ने की आवश्यकता थी , बल्कि मुझे एक वादा-आधारित रूपरेखा के साथ काम करने की भी आवश्यकता थी जिसने त्रुटि पर वादों को अस्वीकार कर दिया।
कार्विक

यह मेरे लिए काम नहीं किया। लगता है कि अगर url विफल रहता है तो ब्लॉक को पकड़ने में नहीं जा रहा है। [प्रतिक्रिया] = प्रतीक्षा oauthGet ( ${host}/user/permissions/repositories_wrong_url/, accessToken, accessTokenSecret) .catch (ग़लती => {logger.error ('रिपॉजिटरी अनुमतियां प्राप्त करने में असमर्थ', इर); कॉलबैक (ग़लती);})
sn.anurag

1
awaitयहां कीवर्ड की जरूरत नहीं है।
आशीष रावत

12

आप एक आवरण फ़ंक्शन बना सकते हैं जो एक वादा करता है और डेटा के साथ एक सरणी देता है अगर कोई त्रुटि नहीं होती है और त्रुटि होती है।

function safePromise(promise) {
  return promise.then(data => [ data ]).catch(error => [ null, error ]);
}

ES7 में और एक async फ़ंक्शन में इसका उपयोग करें :

async function checkItem() {
  const [ item, error ] = await safePromise(getItem(id));
  if (error) { return null; } // handle error and return
  return item; // no error so safe to use item
}

1
एक सुंदर गो वाक्यविन्यास करने की कोशिश की तरह लग रहा है लेकिन बहुत लालित्य के बिना। मुझे लगता है कि इसका उपयोग करने वाले कोड को समाधान के बाहर मूल्य को चूसने के लिए पर्याप्त रूप से बाधित होना चाहिए।
किम

8

Async फ़ंक्शन को लिखने का एक बेहतर तरीका शुरू से लंबित वादा वापस करना और फिर वादे के कॉलबैक के भीतर अस्वीकार और प्रस्तावों दोनों को संभालना होगा, बजाय कि त्रुटि पर एक अस्वीकार किए गए वादे को थूकना होगा। उदाहरण:

async foo(id: string): Promise<A> {
    return new Promise(function(resolve, reject) {
        // execute some code here
        if (success) { // let's say this is a boolean value from line above
            return resolve(success);
        } else {
            return reject(error); // this can be anything, preferably an Error object to catch the stacktrace from this function
        }
    });
}

फिर आप लौटे वादे पर सिर्फ चेन के तरीके:

async function bar () {
    try {
        var result = await foo("someID")
        // use the result here
    } catch (error) {
        // handle error here
    }
}

bar()

स्रोत - यह ट्यूटोरियल:

https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise


5
विशेष रूप से async / प्रतीक्षा का उपयोग करने के बारे में पूछा गया प्रश्न। वादों का उपयोग नहीं
माक

यह उत्तर निश्चित सही उत्तर नहीं था। यह ऊपर दिए गए अन्य उत्तरों के लिए एक समर्थन उत्तर था। मैंने इसे एक टिप्पणी के रूप में रखा होगा लेकिन यह देखते हुए कि मेरे पास कोड है, उत्तर क्षेत्र बेहतर जगह है।
ओजीजियंटिएंट

स्पष्टीकरण देने के लिए धन्यवाद। यह दिखाते हैं कि कैसे एक async फ़ंक्शन करने के लिए निश्चित रूप से उपयोगी है। प्रतीक्षा का उपयोग करने के लिए दूसरे कोड ब्लॉक को अपडेट करना बहुत अधिक प्रासंगिक और उपयोगी होगा। चीयर्स
मैक

मैंने इसे अद्यतन करने के लिए आपकी प्रतिक्रिया संपादित की है। अगर मुझे कुछ याद है तो मुझे बताएं
Mak

4

मेरे पास एक उपन्यास दृष्टिकोण को ठीक से संभालने का सुझाव है , जिसमें कई प्रयास-पकड़ने वाले ब्लॉक नहीं हैं।

import to from './to';

async foo(id: string): Promise<A> {
    let err, result;
    [err, result] = await to(someAsyncPromise()); // notice the to() here
    if (err) {
        return 400;
    }
    return 200;
}

जहां। Tots फ़ंक्शन को यहां से आयात किया जाना चाहिए:

export default function to(promise: Promise<any>): Promise<any> {
    return promise.then(data => {
        return [null, data];
    }).catch(err => [err]);
}

क्रेडिट निम्न लिंक में दीमा ग्रॉसमैन के पास जाता है ।


1
मैं इस निर्माण का उपयोग लगभग विशेष रूप से (बहुत क्लीनर) करता हूं और एक 'टू' मॉड्यूल है जो थोड़ी देर के लिए npmjs.com/package/await-to-js के आसपास रहा है । अलग-अलग घोषणा की जरूरत नहीं है बस deconstructed असाइनमेंट के सामने आने दें। let [err]=केवल त्रुटियों के लिए जाँच करने पर भी कर सकते हैं।
21

3

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

यदि आपका कोड async/ का उपयोग कर रहा है await, तो इसके Errorबजाय इसे अस्वीकार करना एक अच्छा अभ्यास है 400:

try {
  await foo('a');
}
catch (e) {
  // you would still want `e` to be an `Error` instead of `400`
}

3

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

यह सुनिश्चित करने के लिए कि मैं सिर्फ बालों को विभाजित नहीं कर रहा था, मैंने इस कोड का उपयोग करते हुए तीन अलग-अलग तरीकों का एक प्रदर्शन परीक्षण चलाया:

const iterations = 100000;

function getSwitch() {
  return Math.round(Math.random()) === 1;
}

function doSomething(value) {
  return 'something done to ' + value.toString();
}

let processWithThrow = function () {
  if (getSwitch()) {
    throw new Error('foo');
  }
};

let processWithReturn = function () {
  if (getSwitch()) {
    return new Error('bar');
  } else {
    return {}
  }
};

let processWithCustomObject = function () {
  if (getSwitch()) {
    return {type: 'rejection', message: 'quux'};
  } else {
    return {type: 'usable response', value: 'fnord'};
  }
};

function testTryCatch(limit) {
  for (let i = 0; i < limit; i++) {
    try {
      processWithThrow();
    } catch (e) {
      const dummyValue = doSomething(e);
    }
  }
}

function testReturnError(limit) {
  for (let i = 0; i < limit; i++) {
    const returnValue = processWithReturn();
    if (returnValue instanceof Error) {
      const dummyValue = doSomething(returnValue);
    }
  }
}

function testCustomObject(limit) {
  for (let i = 0; i < limit; i++) {
    const returnValue = processWithCustomObject();
    if (returnValue.type === 'rejection') {
      const dummyValue = doSomething(returnValue);
    }
  }
}

let start, end;
start = new Date();
testTryCatch(iterations);
end = new Date();
const interval_1 = end - start;
start = new Date();
testReturnError(iterations);
end = new Date();
const interval_2 = end - start;
start = new Date();
testCustomObject(iterations);
end = new Date();
const interval_3 = end - start;

console.log(`with try/catch: ${interval_1}ms; with returned Error: ${interval_2}ms; with custom object: ${interval_3}ms`);

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

मेरे परिणाम थे:

with try/catch: 507ms; with returned Error: 260ms; with custom object: 5ms

मुझे पता है कि ऐसे बहुत से मामले हैं जहाँ छोटे अनुकूलन का शिकार होना मुश्किल नहीं है, लेकिन बड़े पैमाने पर सिस्टम में ये चीजें एक बड़ा संचयी अंतर ला सकती हैं, और यह एक बहुत ही बढ़िया तुलना है।

SO ... जबकि मुझे लगता है कि स्वीकृत उत्तर का दृष्टिकोण उन मामलों में ध्वनि है, जहां आप एक async फ़ंक्शन के भीतर अप्रत्याशित त्रुटियों को संभालने की अपेक्षा कर रहे हैं, ऐसे मामलों में जहां अस्वीकृति का सीधा अर्थ है "आपको प्लान बी (या) के साथ जाना होगा सी, या डी ...) "मुझे लगता है कि मेरी पसंद एक कस्टम प्रतिक्रिया ऑब्जेक्ट का उपयोग करके अस्वीकार करना होगा।


2
यह भी याद रखें कि आपको किसी async फ़ंक्शन के भीतर अप्रत्याशित त्रुटियों से निपटने के बारे में तनावग्रस्त होने की आवश्यकता नहीं है, यदि उस फ़ंक्शन के लिए कॉल एन्कोडिंग क्षेत्र में एक कोशिश / कैच ब्लॉक के भीतर है - वादों के विपरीत - async फ़ंक्शन बबल त्रुटियों को फेंक देता है। दायरे को घेरना, जहाँ वे उस दायरे के लिए स्थानीय त्रुटियों की तरह ही संभाले जाते हैं। यह async / प्रतीक्षा के मुख्य भत्तों में से एक है!
रिकव

माइक्रोबेन्चमार्क शैतान हैं। संख्याओं के करीब देखें। यहाँ 1ms अंतर पर ध्यान देने के लिए आपको कुछ 1000x करने की आवश्यकता है। हां, थ्रो / कैच जोड़ने से फंक्शन को ख़राब किया जाएगा। लेकिन क) यदि आप किसी ऐसी चीज के लिए इंतजार कर रहे हैं, जिसकी पृष्ठभूमि में होने के लिए परिमाण के कई ऑर्डर लेने की संभावना 0.0005 एमएस से अधिक है। b) आपको यहां 1ms अंतर करने के लिए 1000x करने की आवश्यकता है।
जेमी पाट
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.