Skip to content

Commit 6446050

Browse files
committed
introduce executeStreamIterator
avoids needlessly rechecking whether a stream has begun
1 parent c7d216e commit 6446050

File tree

1 file changed

+106
-82
lines changed

1 file changed

+106
-82
lines changed

src/execution/execute.ts

Lines changed: 106 additions & 82 deletions
Original file line numberDiff line numberDiff line change
@@ -1195,33 +1195,39 @@ function completeListValue(
11951195
// This is specified as a simple map, however we're optimizing the path
11961196
// where the list contains no Promises by avoiding creating another Promise.
11971197
let containsPromise = false;
1198-
let previousAsyncPayloadRecord = asyncPayloadRecord;
11991198
const completedResults: Array<unknown> = [];
1199+
const iterator = result[Symbol.iterator]();
12001200
let index = 0;
1201-
for (const item of result) {
1201+
// eslint-disable-next-line no-constant-condition
1202+
while (true) {
12021203
// No need to modify the info object containing the path,
12031204
// since from here on it is not ever accessed by resolver functions.
1204-
const itemPath = exeContext.addPath(path, index, undefined);
12051205

12061206
if (
12071207
stream &&
12081208
typeof stream.initialCount === 'number' &&
12091209
index >= stream.initialCount
12101210
) {
1211-
previousAsyncPayloadRecord = executeStreamField(
1212-
path,
1213-
itemPath,
1214-
item,
1211+
executeStreamIterator(
1212+
index,
1213+
iterator,
12151214
exeContext,
12161215
fieldGroup,
12171216
info,
12181217
itemType,
1219-
previousAsyncPayloadRecord,
1218+
path,
1219+
asyncPayloadRecord,
12201220
);
1221-
index++;
1222-
continue;
1221+
break;
1222+
}
1223+
1224+
const { done, value: item } = iterator.next();
1225+
if (done) {
1226+
break;
12231227
}
12241228

1229+
const itemPath = exeContext.addPath(path, index, undefined);
1230+
12251231
if (
12261232
completeListItemValue(
12271233
item,
@@ -1886,107 +1892,125 @@ function executeDeferredFragment(
18861892
asyncPayloadRecord.addData(promiseOrData);
18871893
}
18881894

1889-
function executeStreamField(
1890-
path: Path,
1891-
itemPath: Path,
1892-
item: PromiseOrValue<unknown>,
1895+
function executeStreamIterator(
1896+
initialIndex: number,
1897+
iterator: Iterator<unknown>,
18931898
exeContext: ExecutionContext,
18941899
fieldGroup: FieldGroup,
18951900
info: GraphQLResolveInfo,
18961901
itemType: GraphQLOutputType,
1902+
path: Path,
18971903
parentContext?: AsyncPayloadRecord,
1898-
): AsyncPayloadRecord {
1899-
const asyncPayloadRecord = new StreamRecord({
1900-
deferDepth: parentContext?.deferDepth,
1901-
path: itemPath,
1902-
parentContext,
1903-
exeContext,
1904-
});
1905-
if (isPromise(item)) {
1906-
const completedItems = completePromisedValue(
1907-
exeContext,
1908-
itemType,
1909-
fieldGroup,
1910-
info,
1911-
itemPath,
1912-
item,
1913-
asyncPayloadRecord,
1914-
).then(
1915-
(value) => [value],
1916-
(error) => {
1917-
asyncPayloadRecord.errors.push(error);
1918-
filterSubsequentPayloads(exeContext, path, asyncPayloadRecord);
1919-
return null;
1920-
},
1921-
);
1904+
): void {
1905+
let index = initialIndex;
1906+
const deferDepth = parentContext?.deferDepth;
1907+
let previousAsyncPayloadRecord = parentContext ?? undefined;
1908+
// eslint-disable-next-line no-constant-condition
1909+
while (true) {
1910+
const { done, value: item } = iterator.next();
1911+
if (done) {
1912+
break;
1913+
}
19221914

1923-
asyncPayloadRecord.addItems(completedItems);
1924-
return asyncPayloadRecord;
1925-
}
1915+
const itemPath = exeContext.addPath(path, index, undefined);
1916+
const asyncPayloadRecord = new StreamRecord({
1917+
deferDepth,
1918+
path: itemPath,
1919+
parentContext: previousAsyncPayloadRecord,
1920+
exeContext,
1921+
});
19261922

1927-
let completedItem: PromiseOrValue<unknown>;
1928-
try {
1929-
try {
1930-
completedItem = completeValue(
1923+
if (isPromise(item)) {
1924+
const completedItems = completePromisedValue(
19311925
exeContext,
19321926
itemType,
19331927
fieldGroup,
19341928
info,
19351929
itemPath,
19361930
item,
19371931
asyncPayloadRecord,
1932+
).then(
1933+
(value) => [value],
1934+
(error) => {
1935+
asyncPayloadRecord.errors.push(error);
1936+
filterSubsequentPayloads(exeContext, path, asyncPayloadRecord);
1937+
return null;
1938+
},
19381939
);
1939-
} catch (rawError) {
1940-
const error = locatedError(
1941-
rawError,
1942-
toNodes(fieldGroup),
1943-
pathToArray(itemPath),
1944-
);
1945-
completedItem = handleFieldError(
1946-
error,
1947-
itemType,
1948-
asyncPayloadRecord.errors,
1949-
);
1950-
filterSubsequentPayloads(exeContext, itemPath, asyncPayloadRecord);
1940+
1941+
asyncPayloadRecord.addItems(completedItems);
1942+
previousAsyncPayloadRecord = asyncPayloadRecord;
1943+
index++;
1944+
continue;
19511945
}
1952-
} catch (error) {
1953-
asyncPayloadRecord.errors.push(error);
1954-
filterSubsequentPayloads(exeContext, path, asyncPayloadRecord);
1955-
asyncPayloadRecord.addItems(null);
1956-
return asyncPayloadRecord;
1957-
}
19581946

1959-
if (isPromise(completedItem)) {
1960-
const completedItems = completedItem
1961-
.then(undefined, (rawError) => {
1947+
let completedItem: PromiseOrValue<unknown>;
1948+
try {
1949+
try {
1950+
completedItem = completeValue(
1951+
exeContext,
1952+
itemType,
1953+
fieldGroup,
1954+
info,
1955+
itemPath,
1956+
item,
1957+
asyncPayloadRecord,
1958+
);
1959+
} catch (rawError) {
19621960
const error = locatedError(
19631961
rawError,
19641962
toNodes(fieldGroup),
19651963
pathToArray(itemPath),
19661964
);
1967-
const handledError = handleFieldError(
1965+
completedItem = handleFieldError(
19681966
error,
19691967
itemType,
19701968
asyncPayloadRecord.errors,
19711969
);
19721970
filterSubsequentPayloads(exeContext, itemPath, asyncPayloadRecord);
1973-
return handledError;
1974-
})
1975-
.then(
1976-
(value) => [value],
1977-
(error) => {
1978-
asyncPayloadRecord.errors.push(error);
1979-
filterSubsequentPayloads(exeContext, path, asyncPayloadRecord);
1980-
return null;
1981-
},
1982-
);
1971+
}
1972+
} catch (error) {
1973+
asyncPayloadRecord.errors.push(error);
1974+
filterSubsequentPayloads(exeContext, path, asyncPayloadRecord);
1975+
asyncPayloadRecord.addItems(null);
1976+
previousAsyncPayloadRecord = asyncPayloadRecord;
1977+
index++;
1978+
continue;
1979+
}
19831980

1984-
asyncPayloadRecord.addItems(completedItems);
1985-
return asyncPayloadRecord;
1986-
}
1981+
if (isPromise(completedItem)) {
1982+
const completedItems = completedItem
1983+
.then(undefined, (rawError) => {
1984+
const error = locatedError(
1985+
rawError,
1986+
toNodes(fieldGroup),
1987+
pathToArray(itemPath),
1988+
);
1989+
const handledError = handleFieldError(
1990+
error,
1991+
itemType,
1992+
asyncPayloadRecord.errors,
1993+
);
1994+
filterSubsequentPayloads(exeContext, itemPath, asyncPayloadRecord);
1995+
return handledError;
1996+
})
1997+
.then(
1998+
(value) => [value],
1999+
(error) => {
2000+
asyncPayloadRecord.errors.push(error);
2001+
filterSubsequentPayloads(exeContext, path, asyncPayloadRecord);
2002+
return null;
2003+
},
2004+
);
2005+
2006+
asyncPayloadRecord.addItems(completedItems);
2007+
} else {
2008+
asyncPayloadRecord.addItems([completedItem]);
2009+
}
19872010

1988-
asyncPayloadRecord.addItems([completedItem]);
1989-
return asyncPayloadRecord;
2011+
previousAsyncPayloadRecord = asyncPayloadRecord;
2012+
index++;
2013+
}
19902014
}
19912015

19922016
async function executeStreamAsyncIteratorItem(

0 commit comments

Comments
 (0)