@@ -49,6 +49,11 @@ export interface ReadableStream<T> extends ReadableStreamEvents<T> {
4949 * Destroys the stream and stops emitting any event.
5050 */
5151 destroy ( ) : void ;
52+
53+ /**
54+ * Allows to remove a listener that was previously added.
55+ */
56+ removeListener ( event : string , callback : Function ) : void ;
5257}
5358
5459/**
@@ -74,8 +79,14 @@ export interface WriteableStream<T> extends ReadableStream<T> {
7479 * Writing data to the stream will trigger the on('data')
7580 * event listener if the stream is flowing and buffer the
7681 * data otherwise until the stream is flowing.
82+ *
83+ * If a `highWaterMark` is configured and writing to the
84+ * stream reaches this mark, a promise will be returned
85+ * that should be awaited on before writing more data.
86+ * Otherwise there is a risk of buffering a large number
87+ * of data chunks without consumer.
7788 */
78- write ( data : T ) : void ;
89+ write ( data : T ) : void | Promise < void > ;
7990
8091 /**
8192 * Signals an error to the consumer of the stream via the
@@ -118,8 +129,18 @@ export interface ITransformer<Original, Transformed> {
118129 error ?: IErrorTransformer ;
119130}
120131
121- export function newWriteableStream < T > ( reducer : IReducer < T > ) : WriteableStream < T > {
122- return new WriteableStreamImpl < T > ( reducer ) ;
132+ export function newWriteableStream < T > ( reducer : IReducer < T > , options ?: WriteableStreamOptions ) : WriteableStream < T > {
133+ return new WriteableStreamImpl < T > ( reducer , options ) ;
134+ }
135+
136+ export interface WriteableStreamOptions {
137+
138+ /**
139+ * The number of objects to buffer before WriteableStream#write()
140+ * signals back that the buffer is full. Can be used to reduce
141+ * the memory pressure when the stream is not flowing.
142+ */
143+ highWaterMark ?: number ;
123144}
124145
125146class WriteableStreamImpl < T > implements WriteableStream < T > {
@@ -141,7 +162,9 @@ class WriteableStreamImpl<T> implements WriteableStream<T> {
141162 end : [ ] as { ( ) : void } [ ]
142163 } ;
143164
144- constructor ( private reducer : IReducer < T > ) { }
165+ private readonly pendingWritePromises : Function [ ] = [ ] ;
166+
167+ constructor ( private reducer : IReducer < T > , private options ?: WriteableStreamOptions ) { }
145168
146169 pause ( ) : void {
147170 if ( this . state . destroyed ) {
@@ -166,7 +189,7 @@ class WriteableStreamImpl<T> implements WriteableStream<T> {
166189 }
167190 }
168191
169- write ( data : T ) : void {
192+ write ( data : T ) : void | Promise < void > {
170193 if ( this . state . destroyed ) {
171194 return ;
172195 }
@@ -179,6 +202,11 @@ class WriteableStreamImpl<T> implements WriteableStream<T> {
179202 // not yet flowing: buffer data until flowing
180203 else {
181204 this . buffer . data . push ( data ) ;
205+
206+ // highWaterMark: if configured, signal back when buffer reached limits
207+ if ( typeof this . options ?. highWaterMark === 'number' && this . buffer . data . length > this . options . highWaterMark ) {
208+ return new Promise ( resolve => this . pendingWritePromises . push ( resolve ) ) ;
209+ }
182210 }
183211 }
184212
@@ -267,13 +295,47 @@ class WriteableStreamImpl<T> implements WriteableStream<T> {
267295 }
268296 }
269297
298+ removeListener ( event : string , callback : Function ) : void {
299+ if ( this . state . destroyed ) {
300+ return ;
301+ }
302+
303+ let listeners : unknown [ ] | undefined = undefined ;
304+
305+ switch ( event ) {
306+ case 'data' :
307+ listeners = this . listeners . data ;
308+ break ;
309+
310+ case 'end' :
311+ listeners = this . listeners . end ;
312+ break ;
313+
314+ case 'error' :
315+ listeners = this . listeners . error ;
316+ break ;
317+ }
318+
319+ if ( listeners ) {
320+ const index = listeners . indexOf ( callback ) ;
321+ if ( index >= 0 ) {
322+ listeners . splice ( index , 1 ) ;
323+ }
324+ }
325+ }
326+
270327 private flowData ( ) : void {
271328 if ( this . buffer . data . length > 0 ) {
272329 const fullDataBuffer = this . reducer ( this . buffer . data ) ;
273330
274331 this . listeners . data . forEach ( listener => listener ( fullDataBuffer ) ) ;
275332
276333 this . buffer . data . length = 0 ;
334+
335+ // When the buffer is empty, resolve all pending writers
336+ const pendingWritePromises = [ ...this . pendingWritePromises ] ;
337+ this . pendingWritePromises . length = 0 ;
338+ pendingWritePromises . forEach ( pendingWritePromise => pendingWritePromise ( ) ) ;
277339 }
278340 }
279341
@@ -308,6 +370,8 @@ class WriteableStreamImpl<T> implements WriteableStream<T> {
308370 this . listeners . data . length = 0 ;
309371 this . listeners . error . length = 0 ;
310372 this . listeners . end . length = 0 ;
373+
374+ this . pendingWritePromises . length = 0 ;
311375 }
312376 }
313377}
0 commit comments