@@ -64,6 +64,8 @@ ObjectSetPrototypeOf(Writable, Stream);
64
64
65
65
function nop ( ) { }
66
66
67
+ const kOnFinished = Symbol ( 'kOnFinished' ) ;
68
+
67
69
function WritableState ( options , stream , isDuplex ) {
68
70
// Duplex streams are both readable and writable, but share
69
71
// the same options object.
@@ -185,6 +187,8 @@ function WritableState(options, stream, isDuplex) {
185
187
// True if close has been emitted or would have been emitted
186
188
// depending on emitClose.
187
189
this . closeEmitted = false ;
190
+
191
+ this [ kOnFinished ] = [ ] ;
188
192
}
189
193
190
194
function resetBuffer ( state ) {
@@ -411,7 +415,7 @@ function onwriteError(stream, state, er, cb) {
411
415
// not enabled. Passing `er` here doesn't make sense since
412
416
// it's related to one specific write, not to the buffered
413
417
// writes.
414
- errorBuffer ( state , new ERR_STREAM_DESTROYED ( 'write' ) ) ;
418
+ errorBuffer ( state ) ;
415
419
// This can emit error, but error must always follow cb.
416
420
errorOrDestroy ( stream , er ) ;
417
421
}
@@ -487,14 +491,14 @@ function afterWrite(stream, state, count, cb) {
487
491
}
488
492
489
493
if ( state . destroyed ) {
490
- errorBuffer ( state , new ERR_STREAM_DESTROYED ( 'write' ) ) ;
494
+ errorBuffer ( state ) ;
491
495
}
492
496
493
497
finishMaybe ( stream , state ) ;
494
498
}
495
499
496
500
// If there's something in the buffer waiting, then invoke callbacks.
497
- function errorBuffer ( state , err ) {
501
+ function errorBuffer ( state ) {
498
502
if ( state . writing ) {
499
503
return ;
500
504
}
@@ -503,7 +507,11 @@ function errorBuffer(state, err) {
503
507
const { chunk, callback } = state . buffered [ n ] ;
504
508
const len = state . objectMode ? 1 : chunk . length ;
505
509
state . length -= len ;
506
- callback ( err ) ;
510
+ callback ( new ERR_STREAM_DESTROYED ( 'write' ) ) ;
511
+ }
512
+
513
+ for ( const callback of state [ kOnFinished ] . splice ( 0 ) ) {
514
+ callback ( new ERR_STREAM_DESTROYED ( 'end' ) ) ;
507
515
}
508
516
509
517
resetBuffer ( state ) ;
@@ -611,10 +619,11 @@ Writable.prototype.end = function(chunk, encoding, cb) {
611
619
}
612
620
613
621
if ( typeof cb === 'function' ) {
614
- if ( err || state . finished )
622
+ if ( err || state . finished ) {
615
623
process . nextTick ( cb , err ) ;
616
- else
617
- onFinished ( this , cb ) ;
624
+ } else {
625
+ state [ kOnFinished ] . push ( cb ) ;
626
+ }
618
627
}
619
628
620
629
return this ;
@@ -636,6 +645,9 @@ function callFinal(stream, state) {
636
645
stream . _final ( ( err ) => {
637
646
state . pendingcb -- ;
638
647
if ( err ) {
648
+ for ( const callback of state [ kOnFinished ] . splice ( 0 ) ) {
649
+ callback ( err ) ;
650
+ }
639
651
errorOrDestroy ( stream , err , state . sync ) ;
640
652
} else if ( needFinish ( state ) ) {
641
653
state . prefinished = true ;
@@ -683,6 +695,11 @@ function finish(stream, state) {
683
695
return ;
684
696
685
697
state . finished = true ;
698
+
699
+ for ( const callback of state [ kOnFinished ] . splice ( 0 ) ) {
700
+ callback ( ) ;
701
+ }
702
+
686
703
stream . emit ( 'finish' ) ;
687
704
688
705
if ( state . autoDestroy ) {
@@ -701,26 +718,6 @@ function finish(stream, state) {
701
718
}
702
719
}
703
720
704
- // TODO(ronag): Avoid using events to implement internal logic.
705
- function onFinished ( stream , cb ) {
706
- function onerror ( err ) {
707
- stream . removeListener ( 'finish' , onfinish ) ;
708
- stream . removeListener ( 'error' , onerror ) ;
709
- cb ( err ) ;
710
- if ( stream . listenerCount ( 'error' ) === 0 ) {
711
- stream . emit ( 'error' , err ) ;
712
- }
713
- }
714
-
715
- function onfinish ( ) {
716
- stream . removeListener ( 'finish' , onfinish ) ;
717
- stream . removeListener ( 'error' , onerror ) ;
718
- cb ( ) ;
719
- }
720
- stream . on ( 'finish' , onfinish ) ;
721
- stream . prependListener ( 'error' , onerror ) ;
722
- }
723
-
724
721
ObjectDefineProperties ( Writable . prototype , {
725
722
726
723
destroyed : {
@@ -800,7 +797,7 @@ const destroy = destroyImpl.destroy;
800
797
Writable . prototype . destroy = function ( err , cb ) {
801
798
const state = this . _writableState ;
802
799
if ( ! state . destroyed ) {
803
- process . nextTick ( errorBuffer , state , new ERR_STREAM_DESTROYED ( 'write' ) ) ;
800
+ process . nextTick ( errorBuffer , state ) ;
804
801
}
805
802
destroy . call ( this , err , cb ) ;
806
803
return this ;
0 commit comments