नोड.जेएस धाराओं के साथ हैंडलिंग में त्रुटि


164

धाराओं के साथ त्रुटियों को संभालने का सही तरीका क्या है? मुझे पहले से पता है कि एक 'त्रुटि' घटना है जिस पर आप सुन सकते हैं, लेकिन मैं मनमाने ढंग से जटिल स्थितियों के बारे में कुछ और विवरण जानना चाहता हूं।

शुरुआत के लिए, जब आप एक साधारण पाइप श्रृंखला करना चाहते हैं तो आप क्या करते हैं:

input.pipe(transformA).pipe(transformB).pipe(transformC)...

और आप उन परिवर्तनों में से एक को कैसे ठीक से बनाते हैं ताकि त्रुटियों को सही तरीके से संभाला जाए?

अधिक संबंधित प्रश्न:

  • जब कोई त्रुटि होती है, तो 'अंतिम' घटना क्या होती है? क्या यह कभी नहीं निकाल दिया जाता है? क्या यह कभी-कभी निकाल दिया जाता है? क्या यह परिवर्तन / धारा पर निर्भर करता है? यहां क्या मानक हैं?
  • क्या पाइप के माध्यम से त्रुटियों के प्रचार के लिए कोई तंत्र हैं?
  • क्या डोमेन इस समस्या को प्रभावी ढंग से हल करते हैं? उदाहरण अच्छा होगा।
  • क्या 'एरर' ईवेंट्स से निकली त्रुटियां स्टैक के निशान हैं? कभी कभी? कभी नहीँ? क्या उनमें से एक पाने का एक तरीका है?

1
यह तुच्छ नहीं है। Promiseचौखटे इसे बहुत सरल बनाते हैं
स्लीज़िका

27
दुर्भाग्य से वादे / वायदे वास्तव में धाराओं के साथ आपकी मदद नहीं कर सकते ...
बीटी

जवाबों:


222

परिवर्तन

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


जहाँ आप एक परिवर्तन धारा डाल सकते हैं


ट्रांसफ़ॉर्म स्ट्रीम बनाने के लिए यहाँ और यहाँ देखें । तुमको बस यह करना है :

  1. स्ट्रीम मॉड्यूल शामिल करें
  2. ट्रांसफ़ॉर्म क्लास को तत्काल (या विरासत से)
  3. एक _transformविधि लागू करें जो एक लेता है (chunk, encoding, callback)

चंक आपका डेटा है। यदि आप काम कर रहे हैं तो ज्यादातर समय आपको एन्कोडिंग के बारे में चिंता करने की आवश्यकता नहीं होगी objectMode = true। कॉलबैक तब कहा जाता है जब आपको चंक को संसाधित किया जाता है। यह हिस्सा फिर अगली धारा में धकेल दिया जाता है।

यदि आप एक अच्छा सहायक मॉड्यूल चाहते हैं जो आपको स्ट्रीम के माध्यम से वास्तव में आसानी से करने में सक्षम होगा, तो मैं सुझाव देता हूं कि 2

त्रुटि से निपटने के लिए, पढ़ते रहें।

पाइप

एक पाइप श्रृंखला में, त्रुटियों को संभालना वास्तव में गैर-तुच्छ है। इस थ्रेड के अनुसार .pipe () आगे की त्रुटियों के लिए नहीं बनाया गया है। तो कुछ इस तरह ...

var a = createStream();
a.pipe(b).pipe(c).on('error', function(e){handleError(e)});

... धारा पर त्रुटियों के लिए केवल सुनना होगा c। यदि एक त्रुटि घटना पर उत्सर्जित किया गया था a, जो नीचे पारित नहीं किया जाएगा और वास्तव में, फेंक देगा। इसे सही ढंग से करने के लिए:

var a = createStream();
a.on('error', function(e){handleError(e)})
.pipe(b)
.on('error', function(e){handleError(e)})
.pipe(c)
.on('error', function(e){handleError(e)});

अब, हालांकि दूसरा तरीका अधिक क्रियात्मक है, आप कम से कम इस संदर्भ को रख सकते हैं कि आपकी त्रुटियां कहां हैं। यह आमतौर पर एक अच्छी बात है।

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

समाप्त

जब कोई त्रुटि ईवेंट को निकाल दिया जाता है, तो अंतिम ईवेंट को निकाल नहीं दिया जाएगा (स्पष्ट रूप से)। किसी त्रुटि ईवेंट को छोड़ने से स्ट्रीम समाप्त हो जाएगी।

डोमेन

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

var d = domain.create();
 d.on('error', handleAllErrors);
 d.run(function() {
     fs.createReadStream(tarball)
       .pipe(gzip.Gunzip())
       .pipe(tar.Extract({ path: targetPath }))
       .on('close', cb);
 });

डोमेन की सुंदरता यह है कि वे स्टैक के निशान को संरक्षित करेंगे। हालांकि घटना-धारा इसका अच्छा काम करती है।

आगे पढ़ने के लिए, स्ट्रीम-हैंडबुक देखें । बहुत गहराई में, लेकिन सुपर उपयोगी और सहायक मॉड्यूल के बहुत से कुछ महान लिंक देता है।


यह वास्तव में बहुत अच्छी जानकारी है, धन्यवाद! क्या आप थोड़ा सा जोड़ सकते हैं कि आप एक परिवर्तन धारा क्यों बनाना चाहते हैं और यह मेरे प्रश्न से संबंधित क्यों है?
बीटी

ज़रूर - हालाँकि मुझे लगा कि आपने इससे संबंधित के बारे में पूछा है; )
mshell_lauren

1
पोस्ट इस पर गूगल समूहों पर isaccs द्वारा NodeJS: groups.google.com/d/msg/nodejs/lJYT9hZxFu0/L59CFbqWGyYJ (grokbase नहीं)
jpillora

यह उत्तर पूरी तरह से लिखा गया है। मैं डोमेन सुझाव की जांच करने जा रहा हूं - यह उस तरह का समाधान प्रतीत होता है जैसा मैं देख रहा था।
अर्धविराम

12
ध्यान दें कि आपको .on('error')एक अनाम फ़ंक्शन में हैंडलर को लपेटने की आवश्यकता नहीं है, a.on('error', function(e){handleError(e)})बस हो सकता हैa.on('error', handleError)
टाइमॉक्सली

28

आप नोड उपयोग कर रहे हैं> = v10.0.0 आप उपयोग कर सकते stream.pipeline और stream.finished

उदाहरण के लिए:

const { pipeline, finished } = require('stream');

pipeline(
  input, 
  transformA, 
  transformB, 
  transformC, 
  (err) => {
    if (err) {
      console.error('Pipeline failed', err);
    } else {
      console.log('Pipeline succeeded');
    }
});


finished(input, (err) => {
  if (err) {
    console.error('Stream failed', err);
  } else {
    console.log('Stream is done reading');
  }
});

अधिक चर्चा के लिए इस github PR को देखें ।


1
finishedजब आप pipelineपहले से ही कॉलबैक हैं , तो आप इसका उपयोग क्यों करेंगे ?
मार्कोस परेरा

4
आप पाइप लाइन और व्यक्तिगत धाराओं के बीच त्रुटियों को अलग तरह से संभालना चाह सकते हैं।
श्यूसन

25

डोमेन हटाए जाते हैं। आपको उनकी जरूरत नहीं है।

इस प्रश्न के लिए, परिवर्तन या लेखन के बीच अंतर इतना महत्वपूर्ण नहीं है।

mshell_lauren का उत्तर बहुत अच्छा है, लेकिन एक विकल्प के रूप में आप प्रत्येक स्ट्रीम पर त्रुटि घटना के लिए स्पष्ट रूप से सुन सकते हैं जो आपको लगता है कि त्रुटि हो सकती है। और यदि आप चाहें तो हैंडलर फ़ंक्शन का पुन: उपयोग करें।

var a = createReadableStream()
var b = anotherTypeOfStream()
var c = createWriteStream()

a.on('error', handler)
b.on('error', handler)
c.on('error', handler)

a.pipe(b).pipe(c)

function handler (err) { console.log(err) }

ऐसा करने से कुख्यात अनियोजित अपवाद को रोकता है, उन में से एक को अपनी त्रुटि घटना को आग लगाना चाहिए


3
lol ने 3 अलग-अलग त्रुटि घटनाओं से निपटने में मज़ा किया है और प्रार्थना की है कि जिसने भी 3 अलग-अलग स्ट्रीमिंग लिब को लिखा है वह त्रुटि से निपटने को सही ढंग से कार्यान्वित करे
अलेक्जेंडर मिल्स

4
@ एलेक्स मिल्स 1) 3 घटनाओं को संभालने में क्या समस्या है, और वे "अलग-अलग" क्यों हैं, जब उनका प्रकार एक ही है - errorएक के रूप में अच्छी तरह से तब इस तथ्य के साथ समझौता कर सकते हैं कि प्रत्येक घटना अलग है; 2) देशी Node.js कार्यक्षमता के अलावा अन्य कौन से स्ट्रीमिंग लिबास ऊपर लिखे गए हैं? और 3) इससे कोई फर्क नहीं पड़ता कि वे आंतरिक रूप से घटनाओं को कैसे संभालते हैं, जब यह स्पष्ट रूप से किसी को भी जो पहले से ही है, उसके ऊपर अतिरिक्त त्रुटि हैंडलर संलग्न करने की अनुमति देता है?
एमएन

10

पूरी श्रृंखला से त्रुटियों को एक साधारण फ़ंक्शन का उपयोग करके सबसे सही स्ट्रीम में प्रचारित किया जा सकता है:

function safePipe (readable, transforms) {
    while (transforms.length > 0) {
        var new_readable = transforms.shift();
        readable.on("error", function(e) { new_readable.emit("error", e); });
        readable.pipe(new_readable);
        readable = new_readable;
    }
    return readable;
}

जिसका उपयोग किया जा सकता है जैसे:

safePipe(readable, [ transform1, transform2, ... ]);

5

.on("error", handler)केवल स्ट्रीम त्रुटियों का ध्यान रखता है, लेकिन यदि आप कस्टम ट्रांसफ़ॉर्म स्ट्रीम का उपयोग कर रहे हैं, .on("error", handler)तो अंदर हो रही त्रुटियों को न पकड़ें_transform फ़ंक्शन के । तो एक आवेदन प्रवाह को नियंत्रित करने के लिए कुछ इस तरह कर सकते हैं: -

this_transformफंक्शन में कीवर्ड Streamस्वयं को संदर्भित करता है, जो कि ए EventEmitter। तो आप try catchत्रुटियों को पकड़ने के लिए नीचे की तरह उपयोग कर सकते हैं और बाद में उन्हें कस्टम इवेंट हैंडलर को पास कर सकते हैं।

// CustomTransform.js
CustomTransformStream.prototype._transform = function (data, enc, done) {
  var stream = this
  try {
    // Do your transform code
  } catch (e) {
    // Now based on the error type, with an if or switch statement
    stream.emit("CTError1", e)
    stream.emit("CTError2", e)
  }
  done()
}

// StreamImplementation.js
someReadStream
  .pipe(CustomTransformStream)
  .on("CTError1", function (e) { console.log(e) })
  .on("CTError2", function (e) { /*Lets do something else*/ })
  .pipe(someWriteStream)

इस तरह, आप अपने तर्क और त्रुटि संचालकों को अलग रख सकते हैं। इसके अलावा, आप केवल कुछ त्रुटियों को संभालने और दूसरों की उपेक्षा करने का विकल्प चुन सकते हैं।

अद्यतन
वैकल्पिक: RXJS अवलोकनीय


4

एक डुप्लेक्स स्ट्रीम में कई धाराओं को संयोजित करने के लिए मल्टीपाइप पैकेज का उपयोग करें । और त्रुटियों को एक स्थान पर संभालना।

const pipe = require('multipipe')

// pipe streams
const stream = pipe(streamA, streamB, streamC) 


// centralized error handling
stream.on('error', fn)

1

ट्रांसफ़ॉर्म स्ट्रीम स्ट्रीम मैकेनिक बनाकर और doneत्रुटि को प्रचारित करने के लिए एक तर्क के साथ कॉलबैक का उपयोग करके Node.js पैटर्न का उपयोग करें :

var transformStream1 = new stream.Transform(/*{objectMode: true}*/);

transformStream1.prototype._transform = function (chunk, encoding, done) {
  //var stream = this;

  try {
    // Do your transform code
    /* ... */
  } catch (error) {
    // nodejs style for propagating an error
    return done(error);
  }

  // Here, everything went well
  done();
}

// Let's use the transform stream, assuming `someReadStream`
// and `someWriteStream` have been defined before
someReadStream
  .pipe(transformStream1)
  .on('error', function (error) {
    console.error('Error in transformStream1:');
    console.error(error);
    process.exit(-1);
   })
  .pipe(someWriteStream)
  .on('close', function () {
    console.log('OK.');
    process.exit();
  })
  .on('error', function (error) {
    console.error(error);
    process.exit(-1);
   });

हम्म, तो आप कह रहे हैं कि अगर सभी स्ट्रीम प्रोसेसर इस तरह बनाए गए, तो क्या त्रुटियों का प्रचार होगा?
बीटी

-2

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

https://nodejs.org/dist/latest-v10.x/docs/api/errors.html


धन्यवाद, लेकिन यह इस सवाल का जवाब नहीं देता है।
बीटी

मुझे 40 पेज का दस्तावेज देना मददगार नहीं है। आपको क्या लगता है कि मुझे उस विशाल पृष्ठ का उल्लेख करना चाहिए? इसके अलावा, क्या आपने मेरा प्रश्न पढ़ा है? मेरा सवाल यह नहीं है "क्या धाराओं के साथ काम पकड़ने की कोशिश की जाती है?" मैं पहले से ही अच्छी तरह से जानता हूं कि ट्राइ-प्रोसेसिंग पाइप लाइनों से एसिंक्रोनस त्रुटियों के साथ ट्राइ-कैच काम नहीं करेगा, जैसे।
बीटी
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.