diff --git a/lib/_stream_writable.js b/lib/_stream_writable.js index 3f7989ab097de0..317333a0b762c3 100644 --- a/lib/_stream_writable.js +++ b/lib/_stream_writable.js @@ -175,6 +175,10 @@ function WritableState(options, stream, isDuplex) { // Indicates whether the stream has finished destroying. this.closed = false; + + // True if close has been emitted or would have been emitted + // depending on emitClose. + this.closeEmitted = false; } function resetBuffer(state) { @@ -650,11 +654,10 @@ function finishMaybe(stream, state, sync) { function finish(stream, state) { state.pendingcb--; - if (state.errorEmitted) + // TODO (ronag): Unify with needFinish. + if (state.errorEmitted || state.closeEmitted) return; - // TODO(ronag): This could occur after 'close' is emitted. - state.finished = true; stream.emit('finish'); diff --git a/lib/internal/streams/destroy.js b/lib/internal/streams/destroy.js index 207074a0c2c134..f17034c3b7aac5 100644 --- a/lib/internal/streams/destroy.js +++ b/lib/internal/streams/destroy.js @@ -73,6 +73,10 @@ function emitCloseNT(self) { const r = self._readableState; const w = self._writableState; + if (w) { + w.closeEmitted = true; + } + if ((w && w.emitClose) || (r && r.emitClose)) { self.emit('close'); } @@ -111,15 +115,16 @@ function undestroy() { } if (w) { - w.closed = false; w.destroyed = false; + w.closed = false; + w.closeEmitted = false; w.errored = false; + w.errorEmitted = false; w.ended = false; w.ending = false; w.finalCalled = false; w.prefinished = false; w.finished = false; - w.errorEmitted = false; } } diff --git a/test/parallel/test-stream-writable-finish-destroyed.js b/test/parallel/test-stream-writable-finish-destroyed.js new file mode 100644 index 00000000000000..207b4aa11abfae --- /dev/null +++ b/test/parallel/test-stream-writable-finish-destroyed.js @@ -0,0 +1,33 @@ +'use strict'; + +const common = require('../common'); +const { Writable } = require('stream'); + +{ + const w = new Writable({ + write: common.mustCall((chunk, encoding, cb) => { + w.on('close', common.mustCall(() => { + cb(); + })); + }) + }); + + w.on('finish', common.mustNotCall()); + w.end('asd'); + w.destroy(); +} + +{ + const w = new Writable({ + write: common.mustCall((chunk, encoding, cb) => { + w.on('close', common.mustCall(() => { + cb(); + w.end(); + })); + }) + }); + + w.on('finish', common.mustNotCall()); + w.write('asd'); + w.destroy(); +}