Skip to content

Commit 499a759

Browse files
authored
Replace getPromise with isPromise (#1255)
* Replace getPromise with isPromise Simplifies logic within the executor, uses the %checks predicate from Flow * Improve flow types
1 parent fb3257c commit 499a759

File tree

4 files changed

+60
-75
lines changed

4 files changed

+60
-75
lines changed

src/execution/execute.js

Lines changed: 32 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -9,10 +9,10 @@
99

1010
import { forEach, isCollection } from 'iterall';
1111
import { GraphQLError, locatedError } from '../error';
12-
import getPromise from '../jsutils/getPromise';
1312
import invariant from '../jsutils/invariant';
1413
import isInvalid from '../jsutils/isInvalid';
1514
import isNullish from '../jsutils/isNullish';
15+
import isPromise from '../jsutils/isPromise';
1616
import memoize3 from '../jsutils/memoize3';
1717
import promiseForObject from '../jsutils/promiseForObject';
1818
import promiseReduce from '../jsutils/promiseReduce';
@@ -229,9 +229,8 @@ function buildResponse(
229229
context: ExecutionContext,
230230
data: MaybePromise<ObjMap<mixed> | null>,
231231
) {
232-
const promise = getPromise(data);
233-
if (promise) {
234-
return promise.then(resolved => buildResponse(context, resolved));
232+
if (isPromise(data)) {
233+
return data.then(resolved => buildResponse(context, resolved));
235234
}
236235
return context.errors.length === 0
237236
? { data }
@@ -403,9 +402,8 @@ function executeOperation(
403402
operation.operation === 'mutation'
404403
? executeFieldsSerially(exeContext, type, rootValue, path, fields)
405404
: executeFields(exeContext, type, rootValue, path, fields);
406-
const promise = getPromise(result);
407-
if (promise) {
408-
return promise.then(undefined, error => {
405+
if (isPromise(result)) {
406+
return result.then(undefined, error => {
409407
exeContext.errors.push(error);
410408
return Promise.resolve(null);
411409
});
@@ -484,9 +482,8 @@ function executeFieldsSerially(
484482
if (result === undefined) {
485483
return results;
486484
}
487-
const promise = getPromise(result);
488-
if (promise) {
489-
return promise.then(resolvedResult => {
485+
if (isPromise(result)) {
486+
return result.then(resolvedResult => {
490487
results[responseName] = resolvedResult;
491488
return results;
492489
});
@@ -525,7 +522,7 @@ function executeFields(
525522
return results;
526523
}
527524
results[responseName] = result;
528-
if (getPromise(result)) {
525+
if (!containsPromise && isPromise(result)) {
529526
containsPromise = true;
530527
}
531528
return results;
@@ -684,7 +681,7 @@ function resolveField(
684681
source: mixed,
685682
fieldNodes: $ReadOnlyArray<FieldNode>,
686683
path: ResponsePath,
687-
): mixed {
684+
): MaybePromise<mixed> {
688685
const fieldNode = fieldNodes[0];
689686
const fieldName = fieldNode.name.value;
690687

@@ -773,8 +770,7 @@ export function resolveFieldValueOrError<TSource>(
773770
const context = exeContext.contextValue;
774771

775772
const result = resolveFn(source, args, context, info);
776-
const promise = getPromise(result);
777-
return promise ? promise.then(undefined, asErrorInstance) : result;
773+
return isPromise(result) ? result.then(undefined, asErrorInstance) : result;
778774
} catch (error) {
779775
return asErrorInstance(error);
780776
}
@@ -795,7 +791,7 @@ function completeValueCatchingError(
795791
info: GraphQLResolveInfo,
796792
path: ResponsePath,
797793
result: mixed,
798-
): mixed {
794+
): MaybePromise<mixed> {
799795
// If the field type is non-nullable, then it is resolved without any
800796
// protection from errors, however it still properly locates the error.
801797
if (isNonNullType(returnType)) {
@@ -820,13 +816,12 @@ function completeValueCatchingError(
820816
path,
821817
result,
822818
);
823-
const promise = getPromise(completed);
824-
if (promise) {
819+
if (isPromise(completed)) {
825820
// If `completeValueWithLocatedError` returned a rejected promise, log
826821
// the rejection error and resolve to null.
827822
// Note: we don't rely on a `catch` method, but we do expect "thenable"
828823
// to take a second callback for the error case.
829-
return promise.then(undefined, error => {
824+
return completed.then(undefined, error => {
830825
exeContext.errors.push(error);
831826
return Promise.resolve(null);
832827
});
@@ -849,7 +844,7 @@ function completeValueWithLocatedError(
849844
info: GraphQLResolveInfo,
850845
path: ResponsePath,
851846
result: mixed,
852-
): mixed {
847+
): MaybePromise<mixed> {
853848
try {
854849
const completed = completeValue(
855850
exeContext,
@@ -859,9 +854,8 @@ function completeValueWithLocatedError(
859854
path,
860855
result,
861856
);
862-
const promise = getPromise(completed);
863-
if (promise) {
864-
return promise.then(undefined, error =>
857+
if (isPromise(completed)) {
858+
return completed.then(undefined, error =>
865859
Promise.reject(
866860
locatedError(
867861
asErrorInstance(error),
@@ -909,11 +903,10 @@ function completeValue(
909903
info: GraphQLResolveInfo,
910904
path: ResponsePath,
911905
result: mixed,
912-
): mixed {
906+
): MaybePromise<mixed> {
913907
// If result is a Promise, apply-lift over completeValue.
914-
const promise = getPromise(result);
915-
if (promise) {
916-
return promise.then(resolved =>
908+
if (isPromise(result)) {
909+
return result.then(resolved =>
917910
completeValue(exeContext, returnType, fieldNodes, info, path, resolved),
918911
);
919912
}
@@ -1012,7 +1005,7 @@ function completeListValue(
10121005
info: GraphQLResolveInfo,
10131006
path: ResponsePath,
10141007
result: mixed,
1015-
): mixed {
1008+
): MaybePromise<$ReadOnlyArray<mixed>> {
10161009
invariant(
10171010
isCollection(result),
10181011
`Expected Iterable, but did not find one for field ${
@@ -1038,7 +1031,7 @@ function completeListValue(
10381031
item,
10391032
);
10401033

1041-
if (!containsPromise && getPromise(completedItem)) {
1034+
if (!containsPromise && isPromise(completedItem)) {
10421035
containsPromise = true;
10431036
}
10441037
completedResults.push(completedItem);
@@ -1074,14 +1067,13 @@ function completeAbstractValue(
10741067
info: GraphQLResolveInfo,
10751068
path: ResponsePath,
10761069
result: mixed,
1077-
): mixed {
1070+
): MaybePromise<ObjMap<mixed>> {
10781071
const runtimeType = returnType.resolveType
10791072
? returnType.resolveType(result, exeContext.contextValue, info)
10801073
: defaultResolveTypeFn(result, exeContext.contextValue, info, returnType);
10811074

1082-
const promise = getPromise(runtimeType);
1083-
if (promise) {
1084-
return promise.then(resolvedRuntimeType =>
1075+
if (isPromise(runtimeType)) {
1076+
return runtimeType.then(resolvedRuntimeType =>
10851077
completeObjectValue(
10861078
exeContext,
10871079
ensureValidRuntimeType(
@@ -1103,7 +1095,7 @@ function completeAbstractValue(
11031095
return completeObjectValue(
11041096
exeContext,
11051097
ensureValidRuntimeType(
1106-
((runtimeType: any): ?GraphQLObjectType | string),
1098+
runtimeType,
11071099
exeContext,
11081100
returnType,
11091101
fieldNodes,
@@ -1163,17 +1155,16 @@ function completeObjectValue(
11631155
info: GraphQLResolveInfo,
11641156
path: ResponsePath,
11651157
result: mixed,
1166-
): mixed {
1158+
): MaybePromise<ObjMap<mixed>> {
11671159
// If there is an isTypeOf predicate function, call it with the
11681160
// current result. If isTypeOf returns false, then raise an error rather
11691161
// than continuing execution.
11701162
if (returnType.isTypeOf) {
11711163
const isTypeOf = returnType.isTypeOf(result, exeContext.contextValue, info);
11721164

1173-
const promise = getPromise(isTypeOf);
1174-
if (promise) {
1175-
return promise.then(isTypeOfResult => {
1176-
if (!isTypeOfResult) {
1165+
if (isPromise(isTypeOf)) {
1166+
return isTypeOf.then(resolvedIsTypeOf => {
1167+
if (!resolvedIsTypeOf) {
11771168
throw invalidReturnTypeError(returnType, result, fieldNodes);
11781169
}
11791170
return collectAndExecuteSubfields(
@@ -1220,7 +1211,7 @@ function collectAndExecuteSubfields(
12201211
info: GraphQLResolveInfo,
12211212
path: ResponsePath,
12221213
result: mixed,
1223-
): mixed {
1214+
): MaybePromise<ObjMap<mixed>> {
12241215
// Collect sub-fields to execute to complete this value.
12251216
const subFieldNodes = collectSubfields(exeContext, returnType, fieldNodes);
12261217
return executeFields(exeContext, returnType, result, path, subFieldNodes);
@@ -1289,9 +1280,8 @@ function defaultResolveTypeFn(
12891280
if (type.isTypeOf) {
12901281
const isTypeOfResult = type.isTypeOf(value, context, info);
12911282

1292-
const promise = getPromise(isTypeOfResult);
1293-
if (promise) {
1294-
promisedIsTypeOfResults[i] = promise;
1283+
if (isPromise(isTypeOfResult)) {
1284+
promisedIsTypeOfResults[i] = isTypeOfResult;
12951285
} else if (isTypeOfResult) {
12961286
return type;
12971287
}

src/jsutils/getPromise.js

Lines changed: 0 additions & 24 deletions
This file was deleted.

src/jsutils/isPromise.js

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
/**
2+
* Copyright (c) 2015-present, Facebook, Inc.
3+
*
4+
* This source code is licensed under the MIT license found in the
5+
* LICENSE file in the root directory of this source tree.
6+
*
7+
* @flow strict
8+
*/
9+
10+
/**
11+
* Returns true if the value acts like a Promise, i.e. has a "then" function,
12+
* otherwise returns false.
13+
*/
14+
declare function isPromise(value: mixed): boolean %checks(value instanceof
15+
Promise);
16+
17+
// eslint-disable-next-line no-redeclare
18+
export default function isPromise(value) {
19+
return Boolean(value && typeof value.then === 'function');
20+
}

src/jsutils/promiseReduce.js

Lines changed: 8 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
* @flow strict
88
*/
99

10-
import getPromise from './getPromise';
10+
import isPromise from './isPromise';
1111
import type { MaybePromise } from './MaybePromise';
1212

1313
/**
@@ -22,12 +22,11 @@ export default function promiseReduce<T, U>(
2222
callback: (U, T) => MaybePromise<U>,
2323
initialValue: MaybePromise<U>,
2424
): MaybePromise<U> {
25-
return values.reduce((previous, value) => {
26-
const promise = getPromise(previous);
27-
if (promise) {
28-
return promise.then(resolved => callback(resolved, value));
29-
}
30-
// Previous is not Promise<U>, so it is U.
31-
return callback((previous: any), value);
32-
}, initialValue);
25+
return values.reduce(
26+
(previous, value) =>
27+
isPromise(previous)
28+
? previous.then(resolved => callback(resolved, value))
29+
: callback(previous, value),
30+
initialValue,
31+
);
3332
}

0 commit comments

Comments
 (0)