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ć?
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.
Jak utworzyć strumień transformacji zobacz tutaj i tutaj . Wszystko co musisz zrobić to:
- Dołącz moduł strumienia
- instantiate (or inheritate from) the Transform class
- 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);
});
- powyższy kod pochodzi z tego postu
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.
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.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łądWarning: 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, ... ]);
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
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)
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);
});
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ą:
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