Obsługa błędów za pomocą węzła.strumienie js

Jaki jest prawidłowy sposób radzenia sobie z błędami w strumieniach? Wiem już, że jest Zdarzenie "błąd", na którym możesz słuchać, ale chcę poznać więcej szczegółów na temat arbitralnie skomplikowanych sytuacji.

Na początek, co zrobić, gdy chcesz zrobić prosty łańcuch rurowy:

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

A jak prawidłowo utworzyć jedną z tych transformat, aby błędy były poprawnie obsługiwane?

Więcej pytań pokrewnych:

  • gdy wystąpi błąd, co się stanie z wydarzeniem "koniec"? Nigdy nie zostanie zwolniony? Czy czasem się go zwalnia? Czy to zależy od transformacji / strumienia? Jakie są tu standardy?
  • czy są jakieś mechanizmy do przepompowywania błędów przez rury?
  • czy domeny skutecznie rozwiązują ten problem? Przykłady byłyby miłe.
  • czy błędy wynikające ze zdarzeń 'error' mają ślady stosu? Czasami? Nigdy? jest jakiś sposób, żeby je zdobyć?
Author: B T, 2014-02-14

8 answers

Transformacja

Strumienie transformacji są zarówno czytelne, jak i możliwe do zapisania, a zatem są naprawdę dobrymi "średnimi" strumieniami. Z tego powodu są one czasami określane jako strumienie through. Są one podobne do strumienia dupleksu w ten sposób, z tym wyjątkiem, że zapewniają ładny interfejs do manipulowania danymi, a nie tylko wysyłania ich przez. Celem strumienia transformacji jest manipulowanie danymi podczas przesyłania ich przez strumień. Możesz na przykład wykonać kilka wywołań asynchronicznych lub wyprowadzić kilka pól, zremapować niektóre rzeczy, itp.


Gdzie można umieścić strumień transformacji


Jak utworzyć strumień transformacji zobacz tutaj i tutaj . Wszystko co musisz zrobić to:

  1. Dołącz moduł strumienia
  2. instantiate (or inheritate from) the Transform class
  3. zaimplementuj metodę _transform, która przyjmuje (chunk, encoding, callback).

Fragment to Twoje dane. Przez większość czasu nie musisz się martwić o kodowanie, jeśli pracujesz w objectMode = true. Wywołanie zwrotne jest wywoływane po zakończeniu przetwarzania kawałka. Ten fragment jest następnie popychany do następnego strumienia.

Jeśli potrzebujesz ładnego modułu pomocniczego, który pozwoli Ci naprawdę łatwo przejść przez stream, proponuję throught2.

Do obsługi błędów, Czytaj dalej.

Pipe

W łańcuchu rurowym obsługa błędów nie jest trywialna. Według tego wątku .pipe () nie jest zbudowany do przekazywania błędów. Więc coś w rodzaju ...
var a = createStream();
a.pipe(b).pipe(c).on('error', function(e){handleError(e)});

... będzie nasłuchiwać tylko błędów w strumieniu c. Jeśli błąd został wyemitowany na a, to nie zostanie on przekazany i, w rzeczywistości, rzuci. Aby zrobić to poprawnie:

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)});

Teraz, chociaż drugi sposób jest bardziej gadatliwy, możesz przynajmniej zachować kontekst miejsca, w którym zdarzają się Twoje błędy. To zwykle dobrze.

Jedna biblioteka uważam za pomocną, jeśli masz przypadek, w którym chcesz tylko uchwycić błędy w miejscu docelowym i nie dbasz o to tyle o tym, gdzie to się stało, to event-stream .

End

Gdy zostanie wywołane zdarzenie błędu, Zdarzenie końcowe nie zostanie wywołane (jawnie). Emitowanie zdarzenia błędu zakończy strumień.

Domeny

Z mojego doświadczenia wynika, że domeny działają bardzo dobrze przez większość czasu. Jeśli masz nieobsługiwane zdarzenie błędu (tj. emitowanie błędu na strumieniu bez słuchacza), serwer może ulec awarii. Teraz, jak wskazuje powyższy artykuł, możesz zawiń strumień w domenę, która powinna prawidłowo wychwycić wszystkie błędy.

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);
 });

Piękno domen polega na tym, że zachowają one ślady stosu. Chociaż event-stream robi również dobrą robotę.

Aby dowiedzieć się więcej, zapoznaj się zstream-handbook . Całkiem dogłębnie, ale bardzo przydatne i daje świetne linki do wielu przydatnych modułów.

 232
Author: mshell_lauren,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2019-09-19 23:46:57

Jeśli używasz node >= v10. 0. 0 możesz użyć strumienia .rurociąg i strumień .zakończone .

Na przykład:

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');
  }
});
Więcej informacji można znaleźć na stronie github PR.
 30
Author: shusson,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2019-01-09 11:14:28

Domeny są przestarzałe. nie potrzebujesz ich.

W tym pytaniu rozróżnienie między transformacją a zapisywalną nie jest tak ważne.

ODPOWIEDŹ Mshell_lauren jest świetna, ale jako alternatywę możesz również wyraźnie słuchać zdarzenia błędu w każdym strumieniu, który Twoim zdaniem może być błędny. i użyj ponownie funkcji obsługi, jeśli wolisz.

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) }
W ten sposób zapobiega niesławnemu wyjątkowi uncaught, jeśli jeden z tych strumieni wystrzeli błąd
 26
Author: Bent Cardan,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-12-29 10:14:46

Błędy z całego łańcucha mogą być propagowane do strumienia po prawej stronie za pomocą prostej funkcji:

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;
}

Który może być użyty w następujący sposób:

safePipe(readable, [ transform1, transform2, ... ]);
 10
Author: Gleba,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-04-21 14:55:11

.on("error", handler) zajmuje się tylko błędami strumienia, ale jeśli używasz niestandardowych strumieni transformacji, .on("error", handler) nie wychwytywaj błędów występujących wewnątrz funkcji _transform. Można więc zrobić coś takiego do sterowania przepływem aplikacji: -

this słowo kluczowe w funkcji _transform odnosi się do samego Stream, które jest EventEmitter. Możesz więc użyć try catch jak poniżej, aby wychwycić błędy, a następnie przekazać je do niestandardowych programów obsługi zdarzeń.

// 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)

W ten sposób można zachować logikę i procedury obsługi błędów oddzielnie. Możesz również zdecydować się na obsługę tylko niektórych błędów i ignorowanie innych.

UPDATE
Alternatywa: Rxjs Observable

 5
Author: Vikas Gautam,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2019-02-21 19:54:08

Użyj pakietu multippe , aby połączyć kilka strumieni w jeden strumień dupleksu. I obsługi błędów w jednym miejscu.

const pipe = require('multipipe')

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


// centralized error handling
stream.on('error', fn)
 4
Author: Sergey Savenko,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2019-10-30 23:59:50

Użyj Węzła.wzorzec js poprzez stworzenie mechaniki strumienia transformacji i wywołanie jego wywołania zwrotnego done z argumentem w celu propagacji błędu:

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);
   });
 1
Author: Derek,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-09-27 16:09:35

Try catch nie wychwytuje błędów, które wystąpiły w strumieniu, ponieważ są one wyrzucane po zakończeniu kodu wywołującego. możesz zapoznać się z dokumentacją:

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

 -2
Author: Mehran,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2019-04-15 02:48:01