Skip to content

Commit 5c39ee6

Browse files
authored
stream: improve webstream readable async iterator performance
PR-URL: nodejs#49662 Reviewed-By: Matteo Collina <matteo.collina@gmail.com> Reviewed-By: Luigi Pinca <luigipinca@gmail.com> Reviewed-By: Yagiz Nizipli <yagiz@nizipli.com> Reviewed-By: James M Snell <jasnell@gmail.com> Reviewed-By: Benjamin Gruenbaum <benjamingr@gmail.com> Reviewed-By: Moshe Atlow <moshe@atlow.co.il>
1 parent b03a757 commit 5c39ee6

File tree

1 file changed

+44
-29
lines changed

1 file changed

+44
-29
lines changed

lib/internal/webstreams/readablestream.js

+44-29
Original file line numberDiff line numberDiff line change
@@ -477,9 +477,13 @@ class ReadableStream {
477477

478478
// eslint-disable-next-line no-use-before-define
479479
const reader = new ReadableStreamDefaultReader(this);
480-
let done = false;
480+
481+
// No __proto__ here to avoid the performance hit.
482+
const state = {
483+
done: false,
484+
current: undefined,
485+
};
481486
let started = false;
482-
let current;
483487

484488
// The nextSteps function is not an async function in order
485489
// to make it more efficient. Because nextSteps explicitly
@@ -488,7 +492,7 @@ class ReadableStream {
488492
// unnecessary Promise allocations to occur, which just add
489493
// cost.
490494
function nextSteps() {
491-
if (done)
495+
if (state.done)
492496
return PromiseResolve({ done: true, value: undefined });
493497

494498
if (reader[kState].stream === undefined) {
@@ -498,31 +502,15 @@ class ReadableStream {
498502
}
499503
const promise = createDeferredPromise();
500504

501-
readableStreamDefaultReaderRead(reader, {
502-
[kChunk](chunk) {
503-
current = undefined;
504-
promise.resolve({ value: chunk, done: false });
505-
},
506-
[kClose]() {
507-
current = undefined;
508-
done = true;
509-
readableStreamReaderGenericRelease(reader);
510-
promise.resolve({ done: true, value: undefined });
511-
},
512-
[kError](error) {
513-
current = undefined;
514-
done = true;
515-
readableStreamReaderGenericRelease(reader);
516-
promise.reject(error);
517-
},
518-
});
505+
// eslint-disable-next-line no-use-before-define
506+
readableStreamDefaultReaderRead(reader, new ReadableStreamAsyncIteratorReadRequest(reader, state, promise));
519507
return promise.promise;
520508
}
521509

522510
async function returnSteps(value) {
523-
if (done)
511+
if (state.done)
524512
return { done: true, value }; // eslint-disable-line node-core/avoid-prototype-pollution
525-
done = true;
513+
state.done = true;
526514

527515
if (reader[kState].stream === undefined) {
528516
throw new ERR_INVALID_STATE.TypeError(
@@ -559,19 +547,19 @@ class ReadableStream {
559547
// need to investigate if it's a bug in our impl or
560548
// the spec.
561549
if (!started) {
562-
current = PromiseResolve();
550+
state.current = PromiseResolve();
563551
started = true;
564552
}
565-
current = current !== undefined ?
566-
PromisePrototypeThen(current, nextSteps, nextSteps) :
553+
state.current = state.current !== undefined ?
554+
PromisePrototypeThen(state.current, nextSteps, nextSteps) :
567555
nextSteps();
568-
return current;
556+
return state.current;
569557
},
570558

571559
return(error) {
572-
return current ?
560+
return state.current ?
573561
PromisePrototypeThen(
574-
current,
562+
state.current,
575563
() => returnSteps(error),
576564
() => returnSteps(error)) :
577565
returnSteps(error);
@@ -773,6 +761,33 @@ function createReadableStreamBYOBRequest(controller, view) {
773761
return stream;
774762
}
775763

764+
class ReadableStreamAsyncIteratorReadRequest {
765+
constructor(reader, state, promise) {
766+
this.reader = reader;
767+
this.state = state;
768+
this.promise = promise;
769+
}
770+
771+
[kChunk](chunk) {
772+
this.state.current = undefined;
773+
this.promise.resolve({ value: chunk, done: false });
774+
}
775+
776+
[kClose]() {
777+
this.state.current = undefined;
778+
this.state.done = true;
779+
readableStreamReaderGenericRelease(this.reader);
780+
this.promise.resolve({ done: true, value: undefined });
781+
}
782+
783+
[kError](error) {
784+
this.state.current = undefined;
785+
this.state.done = true;
786+
readableStreamReaderGenericRelease(this.reader);
787+
this.promise.reject(error);
788+
}
789+
}
790+
776791
class DefaultReadRequest {
777792
constructor() {
778793
this[kState] = createDeferredPromise();

0 commit comments

Comments
 (0)