Skip to content

Commit 7c5e322

Browse files
rluvatonruyadorno
authored andcommitted
stream: improve webstream readable async iterator performance
PR-URL: #49662 Reviewed-By: Matteo Collina <[email protected]> Reviewed-By: Luigi Pinca <[email protected]> Reviewed-By: Yagiz Nizipli <[email protected]> Reviewed-By: James M Snell <[email protected]> Reviewed-By: Benjamin Gruenbaum <[email protected]> Reviewed-By: Moshe Atlow <[email protected]>
1 parent f611583 commit 7c5e322

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
@@ -479,9 +479,13 @@ class ReadableStream {
479479

480480
// eslint-disable-next-line no-use-before-define
481481
const reader = new ReadableStreamDefaultReader(this);
482-
let done = false;
482+
483+
// No __proto__ here to avoid the performance hit.
484+
const state = {
485+
done: false,
486+
current: undefined,
487+
};
483488
let started = false;
484-
let current;
485489

486490
// The nextSteps function is not an async function in order
487491
// to make it more efficient. Because nextSteps explicitly
@@ -490,7 +494,7 @@ class ReadableStream {
490494
// unnecessary Promise allocations to occur, which just add
491495
// cost.
492496
function nextSteps() {
493-
if (done)
497+
if (state.done)
494498
return PromiseResolve({ done: true, value: undefined });
495499

496500
if (reader[kState].stream === undefined) {
@@ -500,31 +504,15 @@ class ReadableStream {
500504
}
501505
const promise = createDeferredPromise();
502506

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

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

529517
if (reader[kState].stream === undefined) {
530518
throw new ERR_INVALID_STATE.TypeError(
@@ -561,19 +549,19 @@ class ReadableStream {
561549
// need to investigate if it's a bug in our impl or
562550
// the spec.
563551
if (!started) {
564-
current = PromiseResolve();
552+
state.current = PromiseResolve();
565553
started = true;
566554
}
567-
current = current !== undefined ?
568-
PromisePrototypeThen(current, nextSteps, nextSteps) :
555+
state.current = state.current !== undefined ?
556+
PromisePrototypeThen(state.current, nextSteps, nextSteps) :
569557
nextSteps();
570-
return current;
558+
return state.current;
571559
},
572560

573561
return(error) {
574-
return current ?
562+
return state.current ?
575563
PromisePrototypeThen(
576-
current,
564+
state.current,
577565
() => returnSteps(error),
578566
() => returnSteps(error)) :
579567
returnSteps(error);
@@ -774,6 +762,33 @@ function createReadableStreamBYOBRequest(controller, view) {
774762
return stream;
775763
}
776764

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

0 commit comments

Comments
 (0)