Node.js akışlarıyla hata işleme


164

Akışlardaki hataları işlemenin doğru yolu nedir? Dinleyebileceğiniz bir 'hata' olayı olduğunu zaten biliyorum, ancak keyfi olarak karmaşık durumlar hakkında daha fazla ayrıntı öğrenmek istiyorum.

Yeni başlayanlar için basit bir boru zinciri yapmak istediğinizde ne yaparsınız:

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

Ve bu dönüşümlerden birini düzgün bir şekilde nasıl oluşturabilirsiniz, böylece hatalar doğru bir şekilde işlenir?

İlgili diğer sorular:

  • bir hata oluştuğunda, 'son' olayına ne olur? Asla kovulmuyor mu? Bazen kovuluyor mu? Dönüşüm / akışa bağlı mı? Buradaki standartlar nelerdir?
  • borulardan hataları yaymak için herhangi bir mekanizma var mı?
  • alanlar bu sorunu etkili bir şekilde çözüyor mu? Örnekler iyi olurdu.
  • 'olayı' olaylarından çıkan hataların yığın izleri var mı? Ara sıra? Asla? onlardan bir tane almanın bir yolu var mı?

1
Bu önemsiz değil. Promiseçerçeveleri daha basit hale getirir
slezica

27
Ne yazık ki vaatler / vadeli işlemler size akışlarla gerçekten yardımcı olamaz ...
BT

Yanıtlar:


222

dönüştürmek

Dönüşüm akışları hem okunabilir hem de yazılabilir ve bu nedenle gerçekten iyi 'orta' akışlardır. Bu nedenle, bazen throughakış olarak adlandırılırlar . Bu şekilde bir dubleks akışına benzerler, ancak verileri göndermek yerine verileri işlemek için güzel bir arayüz sağlarlar. Dönüştürme akışının amacı, akıştan geçirilirken verileri işlemektir. Örneğin, zaman uyumsuz çağrılar yapmak veya birkaç alan türetmek, bazı şeyleri yeniden eşleştirmek vb. İsteyebilirsiniz.


Bir dönüşüm akışını nereye koyabilirsiniz?


Dönüşüm akışı oluşturma hakkında bilgi için buraya ve buraya bakın . Tüm yapman gereken :

  1. akış modülünü dahil et
  2. Transform sınıfını örnekleme (veya devralma)
  3. _transformalan bir yöntem uygulamak (chunk, encoding, callback).

Parça verilerinizdir. Çalışıyorsanız kodlama konusunda endişelenmenize gerek kalmaz objectMode = true. Öbek işleme işiniz bittiğinde geri arama çağrılır. Bu yığın daha sonra bir sonraki akışa itilir.

Eğer gerçekten gerçekten kolay bir şekilde akış yapmanıza olanak sağlayacak güzel bir yardımcı modül istiyorsanız, size through2 öneriyoruz .

Hata işleme için okumaya devam edin.

boru

Bir boru zincirinde, hataların ele alınması gerçekten önemsizdir. Bu iş parçacığına göre .pipe () hataları iletmek için oluşturulmamıştır. Yani şöyle bir şey ...

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

... yalnızca akıştaki hataları dinlerdi c. Bir hata olayı yayılırsa a, bu aktarılmaz ve aslında atılır. Bunu doğru yapmak için:

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

Şimdi, ikinci yol daha ayrıntılı olsa da, en azından hatalarınızın nerede gerçekleştiğinin bağlamını koruyabilirsiniz. Bu genellikle iyi bir şeydir.

Eğer sadece hedefteki hataları yakalamak istediğiniz bir vakanız varsa ve bunun gerçekleştiği yeri umursamadığınızda olay akışı ise yararlı bulduğum bir kütüphane .

son

Bir hata olayı tetiklendiğinde, son olay tetiklenmez (açıkça). Bir hata olayının yayınlanması akışı sona erdirir.

etki

Deneyimlerime göre, alanlar çoğu zaman gerçekten iyi çalışır. İşlenmemiş bir hata olayınız varsa (örn. Dinleyici olmayan bir akışta hata yayarsa), sunucu çökebilir. Şimdi, yukarıdaki makalede belirtildiği gibi, akışı tüm hataları düzgün bir şekilde yakalaması gereken bir alana sarabilirsiniz.

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

Alanların güzelliği, yığın izlerini koruyacaklarıdır. Olay akışı bununla da iyi bir iş çıkarsa da.

Daha fazla okuma için akış el kitabına bakın . Oldukça derin, ama süper kullanışlı ve birçok yararlı modüle harika bağlantılar veriyor.


Bu gerçekten harika bir bilgi, teşekkürler! Neden bir dönüşüm akışı oluşturmak istediğinizi ve bunun neden sorumla ilgilendiğini biraz ekleyebilir misiniz?
BT

Tabii - siz sorduğunuzdan beri bununla ilgili olduğunu düşündüm; )
mshell_lauren

1
Bunu Google Grupları'nda isaccs tarafından yayınlayın - nodejs: groups.google.com/d/msg/nodejs/lJYT9hZxFu0/L59CFbqWGyYJ (grokbase değil)
jpillora 8:14

Bu cevap mükemmel yazılmıştır. Alan adı önerisini araştıracağım - aradığım bir çözüm gibi görünüyor.
Noktalı virgül

12
.on('error')İşleyiciyi anonim bir işleve sarmanız gerekmediğini unutmayın, yani a.on('error', function(e){handleError(e)})olabilira.on('error', handleError)
timoxley

28

> = V10.0.0 düğümü kullanıyorsanız stream.pipeline ve stream.finished kullanabilirsiniz .

Örneğin:

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

Daha fazla tartışma için bu github PR'ye bakın .


1
finishedYine pipelinede bir geri arama olduğunda neden kullanıyorsunuz ?
Marcos Pereira

4
Boru hattı ve bağımsız akışlar arasındaki hataları farklı şekilde işlemek isteyebilirsiniz.
shusson

25

alan adları kullanımdan kaldırıldı. onlara ihtiyacın yok.

bu soru için, dönüşüm veya yazılabilirler arasındaki ayrımlar o kadar önemli değildir.

mshell_lauren'in cevabı harika, ancak alternatif olarak hata yapabileceğini düşündüğünüz her akıştaki hata olayını da açıkça dinleyebilirsiniz. ve isterseniz işleyici işlevini yeniden kullanın.

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

bu, bu akışlardan birinin hata olayını tetiklemesi durumunda yakalanmamış istisnaları önler


3
lol 3 farklı hata olayları işleme eğlenin ve kim 3 farklı akış libs yazdı hata işleme doğru uygulanan uygulanan kim dua
Alexander Mills

4
@Alex Mills 1) 3 olayı ele alma problemi nedir ve türleri aynı olduğunda neden errorfarklıdırlar? 2) yukarıda yerel Node.js işlevselliği dışında hangi akış kütüphaneleri yazılmıştır? ve 3) herkesin zaten orada olan her şeyin üstüne ek hata işleyicileri eklemesine izin verdiğinde, neden olayları dahili olarak nasıl ele aldıkları önemlidir?
amn

10

Tüm zincirin hataları, basit bir işlev kullanılarak en sağdaki akışa yayılabilir:

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

hangi gibi kullanılabilir:

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

5

.on("error", handler)yalnızca Akış hatalarıyla ilgilenir, ancak özel Dönüşüm akışları kullanıyorsanız, işlev .on("error", handler)içinde meydana gelen hataları yakalamayın _transform. Yani uygulama akışını kontrol etmek için böyle bir şey yapabilirsiniz: -

this_transformişlevindeki anahtar kelime, Streamkendisini ifade eder EventEmitter. Böylece try catchhataları yakalamak ve daha sonra bunları özel olay işleyicilerine iletmek için aşağıdaki gibi kullanabilirsiniz .

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

Bu şekilde, mantık ve hata işleyicilerinizi ayrı tutabilirsiniz. Ayrıca, yalnızca bazı hataları işlemeyi ve diğerlerini yoksaymayı seçebilirsiniz.

GÜNCELLEME
Alternatifi: RXJS Gözlemlenebilir


4

Birden fazla akışı tek bir dubleks akışta birleştirmek için multipipe paketini kullanın . Ve hataları tek bir yerde halledin.

const pipe = require('multipipe')

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


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

1

Bir Transform akışı mekaniği oluşturarak doneve hatayı yaymak için geri çağrısını bir bağımsız değişkenle çağırarak Node.js kalıbını kullanın :

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

Hmm, yani tüm akış işlemcileri bu şekilde oluşturulduysa hatalar yayılır mı diyorsunuz?
BT

-2

Yakalamayı dene, akışta oluşan hataları yakalamaz, çünkü arama kodu zaten çıktıktan sonra atılırlar. belgelere başvurabilirsiniz:

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


Teşekkürler, ama bu soruya hiç cevap vermiyor.
BT

Bana 40 sayfalık bir belge vermek yardımcı olmuyor. O dev sayfada neye başvurmam gerektiğini düşünüyorsun? Ayrıca sorumu okudun mu? Benim sorum "akarsuları yakalamaya çalışmak mı?" Deneme yakalamanın eşzamansız hatalarla, örneğin akış işleme boru hatlarından gelenlerle çalışmadığının farkındayım.
BT
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.