async / प्रतीक्षा के साथ ब्लॉक को पकड़ने / प्रयास करें


116

मैं नोड 7 एसिंक्स / वेट फीचर में खुदाई कर रहा हूं और इस तरह कोड भर में ठोकर खाता रहता हूं

function getQuote() {
  let quote = "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.";
  return quote;
}

async function main() {
  try {
    var quote = await getQuote();
    console.log(quote);
  } catch (error) {
    console.error(error);
  }
}

main();

यह एकमात्र संभावना संकल्प / अस्वीकार या वापसी / async / प्रतीक्षा के साथ फेंकने लगता है, हालांकि, v8 कोशिश / पकड़ ब्लॉक के भीतर कोड का अनुकूलन नहीं करता है?!

क्या कोई विकल्प हैं?


'एक सफल नहीं होने के बाद थ्रो' का क्या मतलब है? यदि यह त्रुटी है? यदि यह अपेक्षित परिणाम नहीं देता है? आप कैच ब्लॉक में पुनर्खरीद कर सकते हैं।
देवदिग

afaik v8 do ऑप्टिमाइज़ / ट्राइ / कैच, थ्रो स्टेटमेंट एक धीमा है
तमस हेगडस

1
मैं अभी भी सवाल नहीं समझता। आप पुराने वादे का इस्तेमाल करते हैं, लेकिन मुझे नहीं लगता कि यह तेजी से होगा। तो आप ट्राइ-कैच के प्रदर्शन को लेकर चिंतित हैं? तो फिर यह async इंतजार के साथ क्या करना है?
तामस हेगडस

मेरे उत्तर की जांच करें मैंने एक क्लीनर दृष्टिकोण प्राप्त करने की कोशिश की
जार्डिलियोर

यहाँ आप इस stackoverflow.com/a/61833084/6482248 पर क्लिक करके सफाई कर सकते हैं
प्रथमेश

जवाबों:


133

वैकल्पिक

इसका एक विकल्प:

async function main() {
  try {
    var quote = await getQuote();
    console.log(quote);
  } catch (error) {
    console.error(error);
  }
}

स्पष्ट रूप से वादों का उपयोग करते हुए कुछ इस तरह होगा:

function main() {
  getQuote().then((quote) => {
    console.log(quote);
  }).catch((error) => {
    console.error(error);
  });
}

या ऐसा कुछ, निरंतरता गुजर शैली का उपयोग करके:

function main() {
  getQuote((error, quote) => {
    if (error) {
      console.error(error);
    } else {
      console.log(quote);
    }
  });
}

मूल उदाहरण

आपका मूल कोड क्या करता है, निष्पादन को निलंबित कर देता है और getQuote()निपटान के लिए दिए गए वादे की प्रतीक्षा करता है । इसके बाद यह निष्पादन जारी रखता है और लौटाए गए मान को लिखता है var quoteऔर फिर उसे प्रिंट करता है यदि वादा हल किया गया था, या एक अपवाद फेंकता है और कैच ब्लॉक को चलाता है जो कि त्रुटि को अस्वीकार करता है यदि वादा अस्वीकार कर दिया गया था।

आप सीधे दूसरे उदाहरण की तरह प्रॉमिस एपीआई का उपयोग करके भी ऐसा कर सकते हैं।

प्रदर्शन

अब, प्रदर्शन के लिए। चलिए इसे टेस्ट करते हैं!

मैंने अभी यह कोड लिखा है - रिटर्न वैल्यू के रूप में f1()देता है 1, अपवाद के रूप में f2()फेंकता 1है:

function f1() {
  return 1;
}

function f2() {
  throw 1;
}

अब उसी कोड को मिलियन बार कॉल करते हैं, पहले f1():

var sum = 0;
for (var i = 0; i < 1e6; i++) {
  try {
    sum += f1();
  } catch (e) {
    sum += e;
  }
}
console.log(sum);

और फिर चलो में बदल f1()जाते हैं f2():

var sum = 0;
for (var i = 0; i < 1e6; i++) {
  try {
    sum += f2();
  } catch (e) {
    sum += e;
  }
}
console.log(sum);

यह मेरे लिए मिला परिणाम है f1:

$ time node throw-test.js 
1000000

real    0m0.073s
user    0m0.070s
sys     0m0.004s

यह मेरे लिए है f2:

$ time node throw-test.js 
1000000

real    0m0.632s
user    0m0.629s
sys     0m0.004s

ऐसा लगता है कि आप कुछ कर सकते हैं जैसे कि एक एकल-थ्रेडेड प्रक्रिया में 2 मिलियन फेंकता है। यदि आप इससे अधिक कर रहे हैं तो आपको इसके बारे में चिंता करने की आवश्यकता हो सकती है।

सारांश

मैं इस तरह की चीजों के बारे में चिंता नहीं करूंगा। अगर इस तरह की चीजों का बहुत उपयोग हो जाता है, तो यह अंततः V8 या स्पाइडरमोंकी या चक्र टीमों द्वारा अनुकूलित हो जाएगी और हर कोई इसका पालन करेगा - ऐसा नहीं है कि यह एक सिद्धांत के रूप में अनुकूलित नहीं है, यह सिर्फ एक समस्या नहीं है।

यहां तक ​​कि अगर यह अनुकूलित नहीं है, तो मैं अभी भी तर्क दूंगा कि यदि आप अपने सीपीयू को नोड में अधिकतम कर रहे हैं, तो आपको संभवतः अपना नंबर सी में क्रंचिंग लिखना चाहिए - यही है, जो अन्य चीजों के साथ देशी ऐडऑन हैं। या शायद नोड.जेक्टिव जैसी चीजें Node.js. की तुलना में नौकरी के लिए बेहतर अनुकूल होंगी

मैं सोच रहा हूं कि ऐसा क्या उपयोग होगा जो इतने सारे अपवादों को फेंकने की जरूरत है। आमतौर पर एक मूल्य को वापस करने के बजाय एक अपवाद फेंकना, ठीक है, एक अपवाद है।


मुझे पता है कि कोड आसानी से वादे के साथ लिखा जा सकता है, जैसा कि उल्लेख किया गया है, मैंने इसे विभिन्न उदाहरणों के आसपास देखा है, इसलिए मैं पूछ रहा हूं। कोशिश / पकड़ के भीतर एक भी ऑपरेशन होने से कोई समस्या नहीं हो सकती है, लेकिन आगे आवेदन तर्क के साथ कई async / प्रतीक्षा कार्य हो सकते हैं।
पैट्रिक

4
@Patrick "हो सकता है" और "होगा" अटकलों और वास्तव में परीक्षण के बीच का अंतर है। मैंने इसे एक ही कथन के लिए परीक्षण किया क्योंकि आपके प्रश्न में यही है लेकिन आप कई बयानों के परीक्षण के लिए मेरे उदाहरणों को आसानी से बदल सकते हैं। मैंने एसिंक्रोनस कोड लिखने के लिए कई अन्य विकल्प भी प्रदान किए, जिनके बारे में आपने भी पूछा था। यदि यह आपके प्रश्न का उत्तर देता है तो आप उत्तर को स्वीकार करने पर विचार कर सकते हैं । इसे योग करने के लिए: निश्चित रूप से अपवाद रिटर्न की तुलना में धीमे हैं लेकिन उनका उपयोग अपवाद होना चाहिए।
आरपी

1
एक अपवाद को फेंकना वास्तव में एक अपवाद माना जाता है। यह कहा जा रहा है, कोड को अपनाया नहीं गया है कि आप एक अपवाद फेंकते हैं या नहीं। प्रदर्शन हिट try catchएक अपवाद को फेंकने से नहीं, उपयोग करने से आता है। जबकि संख्या छोटी है, यह आपके परीक्षणों के अनुसार लगभग 10 गुना धीमा है, जो महत्वहीन नहीं है।
नेपॉक्सक्स

21

वैकल्पिक गोलांग में त्रुटि से निपटने के लिए समान

क्योंकि async / प्रतीक्षा में हुड के तहत वादों का उपयोग करता है, आप इस तरह से थोड़ा उपयोगिता फ़ंक्शन लिख सकते हैं:

export function catchEm(promise) {
  return promise.then(data => [null, data])
    .catch(err => [err]);
}

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

import catchEm from 'utility';

async performAsyncWork() {
  const [err, data] = await catchEm(asyncFunction(arg1, arg2));
  if (err) {
    // handle errors
  } else {
    // use data
  }
}

मैंने एक एनपीएम पैकेज बनाया है जो ठीक ऊपर - npmjs.com/package/@simmo/task
माइक

2
@ माइक आप पहिया का फिर से आविष्कार कर सकते हैं - पहले से ही एक लोकप्रिय पैकेज है जो वास्तव में ऐसा करता है: npmjs.com/package/await-to-js
जैकब कुकुल

21

कोशिश-पकड़ने वाले ब्लॉक का एक विकल्प प्रतीक्षित है- js lib। मैं अक्सर इसका इस्तेमाल करता हूं। उदाहरण के लिए:

import to from 'await-to-js';

async function main(callback) {
    const [err,quote] = await to(getQuote());
    
    if(err || !quote) return callback(new Error('No Quote found'));

    callback(null,quote);

}

ट्राइ-कैच की तुलना में यह सिंटैक्स ज्यादा साफ होता है।


इस की कोशिश की और इसे प्यार करता था। एक नया मॉड्यूल स्थापित करने की कीमत पर स्वच्छ और पठनीय कोड। लेकिन अगर आप बहुत से एसिंक्स फ़ंक्शन लिखने की योजना बना रहे हैं, तो मुझे कहना होगा कि यह एक बढ़िया अतिरिक्त है! धन्यवाद
filipbarak

15
async function main() {
  var getQuoteError
  var quote = await getQuote().catch(err => { getQuoteError = err }

  if (getQuoteError) return console.error(err)

  console.log(quote)
}

वैकल्पिक रूप से आप कर सकते हैं शीर्ष पर एक त्रुटि धारण करने के लिए एक संभावित संस्करण की घोषणा करने के बजाय

if (quote instanceof Error) {
  // ...
}

हालांकि यह काम नहीं करेगा अगर कोई TypeError या Reference error जैसी चीज फेंक दी जाए। आप यह सुनिश्चित कर सकते हैं कि यह एक नियमित त्रुटि है

async function main() {
  var quote = await getQuote().catch(err => {
    console.error(err)      

    return new Error('Error getting quote')
  })

  if (quote instanceOf Error) return quote // get out of here or do whatever

  console.log(quote)
}

इसके लिए मेरी प्राथमिकता एक बड़ी कोशिश करने वाले ब्लॉक में सब कुछ लपेट रही है, जहां कई वादे किए जा रहे हैं, विशेष रूप से उस वादे को त्रुटि को संभालने के लिए इसे बोझिल बना सकते हैं। विकल्प के साथ कई कोशिश-पकड़ने वाले ब्लॉक होते हैं जो मुझे समान रूप से बोझिल लगते हैं


8

एक क्लीनर विकल्प निम्नलिखित होगा:

इस तथ्य के कारण कि प्रत्येक async फ़ंक्शन तकनीकी रूप से एक वादा है

जब आप प्रतीक्षा के साथ उन्हें कॉल करते हैं, तो आप फ़ंक्शन में कैच जोड़ सकते हैं

async function a(){
    let error;

    // log the error on the parent
    await b().catch((err)=>console.log('b.failed'))

    // change an error variable
    await c().catch((err)=>{error=true; console.log(err)})

    // return whatever you want
    return error ? d() : null;
}
a().catch(()=>console.log('main program failed'))

सभी मामलों में त्रुटियों को संभालने के लिए कोई प्रयास करने की आवश्यकता नहीं है, और आपके पास कोई कोड त्रुटि नहीं है, आप माता-पिता में इसे छोड़ सकते हैं !!

मान लें कि आप मोंगोडब के साथ काम कर रहे हैं, अगर कोई त्रुटि है तो आप इसे फंक्शन में हैंडल करना पसंद कर सकते हैं, इसे रैपर बनाने या ट्रायल कैच का उपयोग करने से बुला सकते हैं।


आपके 3 कार्य हैं। एक मान प्राप्त करना और त्रुटि को पकड़ना, दूसरा वह जिसे आप लौटाते हैं यदि कोई त्रुटि नहीं है और अंत में कॉलबैक के साथ पहले फ़ंक्शन पर कॉल करें यह जांचने के लिए कि क्या कोई त्रुटि हुई है। यह सब एक एकल "वादा" द्वारा हल किया गया है। (cb) .catch (cb) या trycatch ब्लॉक।
मुख्य कोशी

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

1
सवाल विफल वादों के बाद अमल करने के लिए नहीं कहता है। यहां आप B का इंतजार करते हैं, फिर C चलाते हैं और D को वापस करते हैं यदि वे गलत हैं। यह क्लीनर कैसे है? C को B की प्रतीक्षा करनी है फिर भी वे एक दूसरे से स्वतंत्र हैं। अगर वे स्वतंत्र हैं तो मुझे ए में एक कारण नहीं दिखता है। यदि वे एक दूसरे पर निर्भर थे तो आप C का निष्पादन रोकना चाहते हैं यदि B विफल होता है, तो .then.catch या try-catch का काम। मुझे लगता है कि वे कुछ भी नहीं लौटाते हैं और ए के लिए पूरी तरह से असंबंधित कुछ अतुल्यकालिक क्रियाएं करते हैं। उन्हें एसिंक्स प्रतीक्षा के साथ क्यों बुलाया जाता है?
मुख्य कोशी

सवाल यह है कि async / प्रतीक्षा का उपयोग करते समय त्रुटियों को संभालने के लिए ब्लॉक पकड़ने की कोशिश करने के लिए विकल्प से संबंधित है। यहाँ उदाहरण वर्णनात्मक होना है और एक उदाहरण के अलावा कुछ भी नहीं है। यह एक क्रमिक तरीके से स्वतंत्र संचालन के व्यक्तिगत संचालन को दर्शाता है जो आमतौर पर कैसे async / प्रतीक्षा का उपयोग किया जाता है। उन्हें async प्रतीक्षा के साथ क्यों बुलाया जाता है, यह केवल यह दिखाने के लिए है कि इसे कैसे संभाला जा सकता है। इसका वर्णनात्मक औचित्य से अधिक है।
जरदिल

2

मैं इस तरह से करना चाहूंगा :)

const sthError = () => Promise.reject('sth error');

const test = opts => {
  return (async () => {

    // do sth
    await sthError();
    return 'ok';

  })().catch(err => {
    console.error(err); // error will be catched there 
  });
};

test().then(ret => {
  console.log(ret);
});

यह त्रुटि से निपटने के समान है co

const test = opts => {
  return co(function*() {

    // do sth
    yield sthError();
    return 'ok';

  }).catch(err => {
    console.error(err);
  });
};

कोड बहुत स्पष्ट आदमी नहीं है, हालांकि दिलचस्प लगता है, क्या आप संपादित कर सकते हैं?
जरदिल

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

0

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

एक वादे के लिए दूसरा तर्क पहले से ही अस्वीकृति / विफलता कॉलबैक है। इसके बजाय इसका उपयोग करना बेहतर और सुरक्षित है।

यहाँ एक टाइपस्क्रिप्ट वन-लाइनर है जिसे मैंने इसे संभालने के लिए लिखा है:

function wait<R, E>(promise: Promise<R>): [R | null, E | null] {
  return (promise.then((data: R) => [data, null], (err: E) => [null, err]) as any) as [R, E];
}

// Usage
const [currUser, currUserError] = await wait<GetCurrentUser_user, GetCurrentUser_errors>(
  apiClient.getCurrentUser()
);
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.