Skip to content

Commit e2c3826

Browse files
committed
introduce completeValueCatchingErrors
and utilize it within executeStreamField
1 parent 6b5c8af commit e2c3826

File tree

2 files changed

+78
-50
lines changed

2 files changed

+78
-50
lines changed

src/execution/__tests__/stream-test.ts

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -483,11 +483,6 @@ describe('Execute: stream directive', () => {
483483
},
484484
],
485485
},
486-
],
487-
hasNext: true,
488-
},
489-
{
490-
incremental: [
491486
{
492487
items: [{ name: 'Leia', id: '3' }],
493488
path: ['friendList', 2],

src/execution/execute.ts

Lines changed: 78 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -915,6 +915,41 @@ function completeValue(
915915
);
916916
}
917917

918+
async function completePromiseCatchingErrors(
919+
exeContext: ExecutionContext,
920+
returnType: GraphQLOutputType,
921+
fieldNodes: ReadonlyArray<FieldNode>,
922+
info: GraphQLResolveInfo,
923+
path: Path,
924+
result: Promise<unknown>,
925+
asyncPayloadRecord?: AsyncPayloadRecord,
926+
): Promise<unknown> {
927+
try {
928+
const resolved = await result;
929+
let completed = completeValue(
930+
exeContext,
931+
returnType,
932+
fieldNodes,
933+
info,
934+
path,
935+
resolved,
936+
asyncPayloadRecord,
937+
);
938+
if (isPromise(completed)) {
939+
// see: https://github.com/tc39/proposal-faster-promise-adoption
940+
// it is faster to await a promise prior to returning it from an async function
941+
completed = await completed;
942+
}
943+
return completed;
944+
} catch (rawError) {
945+
const errors = asyncPayloadRecord?.errors ?? exeContext.errors;
946+
const error = locatedError(rawError, fieldNodes, pathToArray(path));
947+
const handledError = handleFieldError(error, returnType, errors);
948+
filterSubsequentPayloads(exeContext, path, asyncPayloadRecord);
949+
return handledError;
950+
}
951+
}
952+
918953
/**
919954
* Returns an object containing the `@stream` arguments if a field should be
920955
* streamed based on the experimental flag, stream directive present and
@@ -1878,21 +1913,19 @@ function executeStreamField(
18781913
exeContext,
18791914
});
18801915
let completedItem: PromiseOrValue<unknown>;
1881-
try {
1916+
if (isPromise(item)) {
1917+
completedItem = completePromiseCatchingErrors(
1918+
exeContext,
1919+
itemType,
1920+
fieldNodes,
1921+
info,
1922+
itemPath,
1923+
item,
1924+
asyncPayloadRecord,
1925+
);
1926+
} else {
18821927
try {
1883-
if (isPromise(item)) {
1884-
completedItem = item.then((resolved) =>
1885-
completeValue(
1886-
exeContext,
1887-
itemType,
1888-
fieldNodes,
1889-
info,
1890-
itemPath,
1891-
resolved,
1892-
asyncPayloadRecord,
1893-
),
1894-
);
1895-
} else {
1928+
try {
18961929
completedItem = completeValue(
18971930
exeContext,
18981931
itemType,
@@ -1902,40 +1935,40 @@ function executeStreamField(
19021935
item,
19031936
asyncPayloadRecord,
19041937
);
1905-
}
19061938

1907-
if (isPromise(completedItem)) {
1908-
// Note: we don't rely on a `catch` method, but we do expect "thenable"
1909-
// to take a second callback for the error case.
1910-
completedItem = completedItem.then(undefined, (rawError) => {
1911-
const error = locatedError(
1912-
rawError,
1913-
fieldNodes,
1914-
pathToArray(itemPath),
1915-
);
1916-
const handledError = handleFieldError(
1917-
error,
1918-
itemType,
1919-
asyncPayloadRecord.errors,
1920-
);
1921-
filterSubsequentPayloads(exeContext, itemPath, asyncPayloadRecord);
1922-
return handledError;
1923-
});
1939+
if (isPromise(completedItem)) {
1940+
// Note: we don't rely on a `catch` method, but we do expect "thenable"
1941+
// to take a second callback for the error case.
1942+
completedItem = completedItem.then(undefined, (rawError) => {
1943+
const error = locatedError(
1944+
rawError,
1945+
fieldNodes,
1946+
pathToArray(itemPath),
1947+
);
1948+
const handledError = handleFieldError(
1949+
error,
1950+
itemType,
1951+
asyncPayloadRecord.errors,
1952+
);
1953+
filterSubsequentPayloads(exeContext, itemPath, asyncPayloadRecord);
1954+
return handledError;
1955+
});
1956+
}
1957+
} catch (rawError) {
1958+
const error = locatedError(rawError, fieldNodes, pathToArray(itemPath));
1959+
completedItem = handleFieldError(
1960+
error,
1961+
itemType,
1962+
asyncPayloadRecord.errors,
1963+
);
1964+
filterSubsequentPayloads(exeContext, itemPath, asyncPayloadRecord);
19241965
}
1925-
} catch (rawError) {
1926-
const error = locatedError(rawError, fieldNodes, pathToArray(itemPath));
1927-
completedItem = handleFieldError(
1928-
error,
1929-
itemType,
1930-
asyncPayloadRecord.errors,
1931-
);
1932-
filterSubsequentPayloads(exeContext, itemPath, asyncPayloadRecord);
1966+
} catch (error) {
1967+
asyncPayloadRecord.errors.push(error);
1968+
filterSubsequentPayloads(exeContext, path, asyncPayloadRecord);
1969+
asyncPayloadRecord.addItems(null);
1970+
return asyncPayloadRecord;
19331971
}
1934-
} catch (error) {
1935-
asyncPayloadRecord.errors.push(error);
1936-
filterSubsequentPayloads(exeContext, path, asyncPayloadRecord);
1937-
asyncPayloadRecord.addItems(null);
1938-
return asyncPayloadRecord;
19391972
}
19401973

19411974
let completedItems: PromiseOrValue<Array<unknown> | null>;

0 commit comments

Comments
 (0)