stream: improve from perf

PR-URL: https://github.com/nodejs/node/pull/50359
Reviewed-By: Vinícius Lourenço Claro Cardoso <contact@viniciusl.com.br>
Reviewed-By: Robert Nagy <ronagy@icloud.com>
Reviewed-By: Benjamin Gruenbaum <benjamingr@gmail.com>
This commit is contained in:
Raz Luvaton 2023-10-26 11:42:58 +03:00 committed by GitHub
parent 4ddb263654
commit 10d51e82a6
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

View File

@ -36,6 +36,7 @@ function from(Readable, iterable, opts) {
throw new ERR_INVALID_ARG_TYPE('iterable', ['Iterable'], iterable);
}
const readable = new Readable({
objectMode: true,
highWaterMark: 1,
@ -46,11 +47,19 @@ function from(Readable, iterable, opts) {
// Flag to protect against _read
// being called before last iteration completion.
let reading = false;
let isAsyncValues = false;
readable._read = function() {
if (!reading) {
reading = true;
next();
if (isAsync) {
nextAsync();
} else if (isAsyncValues) {
nextSyncWithAsyncValues();
} else {
nextSyncWithSyncValues();
}
}
};
@ -78,29 +87,115 @@ function from(Readable, iterable, opts) {
}
}
async function next() {
// There are a lot of duplication here, it's done on purpose for performance
// reasons - avoid await when not needed.
function nextSyncWithSyncValues() {
for (;;) {
try {
const { value, done } = isAsync ?
await iterator.next() :
iterator.next();
const { value, done } = iterator.next();
if (done) {
readable.push(null);
} else {
const res = (value &&
typeof value.then === 'function') ?
await value :
value;
if (res === null) {
reading = false;
throw new ERR_STREAM_NULL_VALUES();
} else if (readable.push(res)) {
continue;
} else {
reading = false;
}
return;
}
if (value &&
typeof value.then === 'function') {
return changeToAsyncValues(value);
}
if (value === null) {
reading = false;
throw new ERR_STREAM_NULL_VALUES();
}
if (readable.push(value)) {
continue;
}
reading = false;
} catch (err) {
readable.destroy(err);
}
break;
}
}
async function changeToAsyncValues(value) {
isAsyncValues = true;
try {
const res = await value;
if (res === null) {
reading = false;
throw new ERR_STREAM_NULL_VALUES();
}
if (readable.push(res)) {
nextSyncWithAsyncValues();
return;
}
reading = false;
} catch (err) {
readable.destroy(err);
}
}
async function nextSyncWithAsyncValues() {
for (;;) {
try {
const { value, done } = iterator.next();
if (done) {
readable.push(null);
return;
}
const res = (value &&
typeof value.then === 'function') ?
await value :
value;
if (res === null) {
reading = false;
throw new ERR_STREAM_NULL_VALUES();
}
if (readable.push(res)) {
continue;
}
reading = false;
} catch (err) {
readable.destroy(err);
}
break;
}
}
async function nextAsync() {
for (;;) {
try {
const { value, done } = await iterator.next();
if (done) {
readable.push(null);
return;
}
if (value === null) {
reading = false;
throw new ERR_STREAM_NULL_VALUES();
}
if (readable.push(value)) {
continue;
}
reading = false;
} catch (err) {
readable.destroy(err);
}