Skip to content

Commit 40ff40a

Browse files
authored
refactor: introduce completeListItemValue (#3752)
1 parent 41bc274 commit 40ff40a

File tree

1 file changed

+103
-90
lines changed

1 file changed

+103
-90
lines changed

src/execution/execute.ts

Lines changed: 103 additions & 90 deletions
Original file line numberDiff line numberDiff line change
@@ -972,7 +972,7 @@ async function completeAsyncIteratorValue(
972972
const errors = asyncPayloadRecord?.errors ?? exeContext.errors;
973973
const stream = getStreamValues(exeContext, fieldNodes, path);
974974
let containsPromise = false;
975-
const completedResults = [];
975+
const completedResults: Array<unknown> = [];
976976
let index = 0;
977977
// eslint-disable-next-line no-constant-condition
978978
while (true) {
@@ -997,58 +997,34 @@ async function completeAsyncIteratorValue(
997997
}
998998

999999
const itemPath = addPath(path, index, undefined);
1000+
let iteration;
10001001
try {
10011002
// eslint-disable-next-line no-await-in-loop
1002-
const { value, done } = await iterator.next();
1003-
if (done) {
1003+
iteration = await iterator.next();
1004+
if (iteration.done) {
10041005
break;
10051006
}
1006-
1007-
try {
1008-
// TODO can the error checking logic be consolidated with completeListValue?
1009-
const completedItem = completeValue(
1010-
exeContext,
1011-
itemType,
1012-
fieldNodes,
1013-
info,
1014-
itemPath,
1015-
value,
1016-
asyncPayloadRecord,
1017-
);
1018-
if (isPromise(completedItem)) {
1019-
containsPromise = true;
1020-
// Note: we don't rely on a `catch` method, but we do expect "thenable"
1021-
// to take a second callback for the error case.
1022-
completedResults.push(
1023-
completedItem.then(undefined, (rawError) => {
1024-
const error = locatedError(
1025-
rawError,
1026-
fieldNodes,
1027-
pathToArray(itemPath),
1028-
);
1029-
const handledError = handleFieldError(error, itemType, errors);
1030-
filterSubsequentPayloads(
1031-
exeContext,
1032-
itemPath,
1033-
asyncPayloadRecord,
1034-
);
1035-
return handledError;
1036-
}),
1037-
);
1038-
} else {
1039-
completedResults.push(completedItem);
1040-
}
1041-
} catch (rawError) {
1042-
completedResults.push(null);
1043-
const error = locatedError(rawError, fieldNodes, pathToArray(itemPath));
1044-
filterSubsequentPayloads(exeContext, itemPath, asyncPayloadRecord);
1045-
handleFieldError(error, itemType, errors);
1046-
}
10471007
} catch (rawError) {
10481008
const error = locatedError(rawError, fieldNodes, pathToArray(itemPath));
10491009
completedResults.push(handleFieldError(error, itemType, errors));
10501010
break;
10511011
}
1012+
1013+
if (
1014+
completeListItemValue(
1015+
iteration.value,
1016+
completedResults,
1017+
errors,
1018+
exeContext,
1019+
itemType,
1020+
fieldNodes,
1021+
info,
1022+
itemPath,
1023+
asyncPayloadRecord,
1024+
)
1025+
) {
1026+
containsPromise = true;
1027+
}
10521028
index += 1;
10531029
}
10541030
return containsPromise ? Promise.all(completedResults) : completedResults;
@@ -1096,7 +1072,7 @@ function completeListValue(
10961072
// where the list contains no Promises by avoiding creating another Promise.
10971073
let containsPromise = false;
10981074
let previousAsyncPayloadRecord = asyncPayloadRecord;
1099-
const completedResults = [];
1075+
const completedResults: Array<unknown> = [];
11001076
let index = 0;
11011077
for (const item of result) {
11021078
// No need to modify the info object containing the path,
@@ -1123,61 +1099,98 @@ function completeListValue(
11231099
continue;
11241100
}
11251101

1126-
try {
1127-
let completedItem;
1128-
if (isPromise(item)) {
1129-
completedItem = item.then((resolved) =>
1130-
completeValue(
1131-
exeContext,
1132-
itemType,
1133-
fieldNodes,
1134-
info,
1135-
itemPath,
1136-
resolved,
1137-
asyncPayloadRecord,
1138-
),
1139-
);
1140-
} else {
1141-
completedItem = completeValue(
1102+
if (
1103+
completeListItemValue(
1104+
item,
1105+
completedResults,
1106+
errors,
1107+
exeContext,
1108+
itemType,
1109+
fieldNodes,
1110+
info,
1111+
itemPath,
1112+
asyncPayloadRecord,
1113+
)
1114+
) {
1115+
containsPromise = true;
1116+
}
1117+
1118+
index++;
1119+
}
1120+
1121+
return containsPromise ? Promise.all(completedResults) : completedResults;
1122+
}
1123+
1124+
/**
1125+
* Complete a list item value by adding it to the completed results.
1126+
*
1127+
* Returns true if the value is a Promise.
1128+
*/
1129+
function completeListItemValue(
1130+
item: unknown,
1131+
completedResults: Array<unknown>,
1132+
errors: Array<GraphQLError>,
1133+
exeContext: ExecutionContext,
1134+
itemType: GraphQLOutputType,
1135+
fieldNodes: ReadonlyArray<FieldNode>,
1136+
info: GraphQLResolveInfo,
1137+
itemPath: Path,
1138+
asyncPayloadRecord?: AsyncPayloadRecord,
1139+
): boolean {
1140+
try {
1141+
let completedItem;
1142+
if (isPromise(item)) {
1143+
completedItem = item.then((resolved) =>
1144+
completeValue(
11421145
exeContext,
11431146
itemType,
11441147
fieldNodes,
11451148
info,
11461149
itemPath,
1147-
item,
1150+
resolved,
11481151
asyncPayloadRecord,
1149-
);
1150-
}
1152+
),
1153+
);
1154+
} else {
1155+
completedItem = completeValue(
1156+
exeContext,
1157+
itemType,
1158+
fieldNodes,
1159+
info,
1160+
itemPath,
1161+
item,
1162+
asyncPayloadRecord,
1163+
);
1164+
}
11511165

1152-
if (isPromise(completedItem)) {
1153-
containsPromise = true;
1154-
// Note: we don't rely on a `catch` method, but we do expect "thenable"
1155-
// to take a second callback for the error case.
1156-
completedResults.push(
1157-
completedItem.then(undefined, (rawError) => {
1158-
const error = locatedError(
1159-
rawError,
1160-
fieldNodes,
1161-
pathToArray(itemPath),
1162-
);
1163-
const handledError = handleFieldError(error, itemType, errors);
1164-
filterSubsequentPayloads(exeContext, itemPath, asyncPayloadRecord);
1165-
return handledError;
1166-
}),
1167-
);
1168-
} else {
1169-
completedResults.push(completedItem);
1170-
}
1171-
} catch (rawError) {
1172-
const error = locatedError(rawError, fieldNodes, pathToArray(itemPath));
1173-
const handledError = handleFieldError(error, itemType, errors);
1174-
filterSubsequentPayloads(exeContext, itemPath, asyncPayloadRecord);
1175-
completedResults.push(handledError);
1166+
if (isPromise(completedItem)) {
1167+
// Note: we don't rely on a `catch` method, but we do expect "thenable"
1168+
// to take a second callback for the error case.
1169+
completedResults.push(
1170+
completedItem.then(undefined, (rawError) => {
1171+
const error = locatedError(
1172+
rawError,
1173+
fieldNodes,
1174+
pathToArray(itemPath),
1175+
);
1176+
const handledError = handleFieldError(error, itemType, errors);
1177+
filterSubsequentPayloads(exeContext, itemPath, asyncPayloadRecord);
1178+
return handledError;
1179+
}),
1180+
);
1181+
1182+
return true;
11761183
}
1177-
index++;
1184+
1185+
completedResults.push(completedItem);
1186+
} catch (rawError) {
1187+
const error = locatedError(rawError, fieldNodes, pathToArray(itemPath));
1188+
const handledError = handleFieldError(error, itemType, errors);
1189+
filterSubsequentPayloads(exeContext, itemPath, asyncPayloadRecord);
1190+
completedResults.push(handledError);
11781191
}
11791192

1180-
return containsPromise ? Promise.all(completedResults) : completedResults;
1193+
return false;
11811194
}
11821195

11831196
/**

0 commit comments

Comments
 (0)