Skip to content

Commit 51e44db

Browse files
committed
Execute serially supporting sync execution.
Refactors executeFieldsSerially to return MaybePromise Fixes #1195
1 parent 358df97 commit 51e44db

File tree

3 files changed

+84
-40
lines changed

3 files changed

+84
-40
lines changed

src/execution/execute.js

Lines changed: 28 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -9,10 +9,12 @@
99

1010
import { forEach, isCollection } from 'iterall';
1111
import { GraphQLError, locatedError } from '../error';
12+
import getPromise from '../jsutils/getPromise';
1213
import invariant from '../jsutils/invariant';
1314
import isInvalid from '../jsutils/isInvalid';
1415
import isNullish from '../jsutils/isNullish';
1516
import memoize3 from '../jsutils/memoize3';
17+
import promiseReduce from '../jsutils/promiseReduce';
1618
import type { ObjMap } from '../jsutils/ObjMap';
1719
import type { MaybePromise } from '../jsutils/MaybePromise';
1820

@@ -465,33 +467,33 @@ function executeFieldsSerially(
465467
sourceValue: mixed,
466468
path: ResponsePath | void,
467469
fields: ObjMap<Array<FieldNode>>,
468-
): Promise<ObjMap<mixed>> {
469-
return Object.keys(fields).reduce(
470-
(prevPromise, responseName) =>
471-
prevPromise.then(results => {
472-
const fieldNodes = fields[responseName];
473-
const fieldPath = addPath(path, responseName);
474-
const result = resolveField(
475-
exeContext,
476-
parentType,
477-
sourceValue,
478-
fieldNodes,
479-
fieldPath,
480-
);
481-
if (result === undefined) {
482-
return results;
483-
}
484-
const promise = getPromise(result);
485-
if (promise) {
486-
return promise.then(resolvedResult => {
487-
results[responseName] = resolvedResult;
488-
return results;
489-
});
490-
}
491-
results[responseName] = result;
470+
): MaybePromise<ObjMap<mixed>> {
471+
return promiseReduce(
472+
Object.keys(fields),
473+
(results, responseName) => {
474+
const fieldNodes = fields[responseName];
475+
const fieldPath = addPath(path, responseName);
476+
const result = resolveField(
477+
exeContext,
478+
parentType,
479+
sourceValue,
480+
fieldNodes,
481+
fieldPath,
482+
);
483+
if (result === undefined) {
492484
return results;
493-
}),
494-
Promise.resolve({}),
485+
}
486+
const promise = getPromise(result);
487+
if (promise) {
488+
return promise.then(resolvedResult => {
489+
results[responseName] = resolvedResult;
490+
return results;
491+
});
492+
}
493+
results[responseName] = result;
494+
return results;
495+
},
496+
Object.create(null),
495497
);
496498
}
497499

@@ -1346,20 +1348,6 @@ export const defaultFieldResolver: GraphQLFieldResolver<any, *> = function(
13461348
}
13471349
};
13481350

1349-
/**
1350-
* Only returns the value if it acts like a Promise, i.e. has a "then" function,
1351-
* otherwise returns void.
1352-
*/
1353-
function getPromise<T>(value: Promise<T> | mixed): Promise<T> | void {
1354-
if (
1355-
typeof value === 'object' &&
1356-
value !== null &&
1357-
typeof value.then === 'function'
1358-
) {
1359-
return (value: any);
1360-
}
1361-
}
1362-
13631351
/**
13641352
* This method looks up the field on the given type defintion.
13651353
* It has special casing for the two introspection fields, __schema

src/jsutils/getPromise.js

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
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
8+
*/
9+
10+
/**
11+
* Only returns the value if it acts like a Promise, i.e. has a "then" function,
12+
* otherwise returns void.
13+
*/
14+
export default function getPromise<T>(
15+
value: Promise<T> | mixed,
16+
): Promise<T> | void {
17+
if (
18+
typeof value === 'object' &&
19+
value !== null &&
20+
typeof value.then === 'function'
21+
) {
22+
return (value: any);
23+
}
24+
}

src/jsutils/promiseReduce.js

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
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
8+
*/
9+
10+
import getPromise from './getPromise';
11+
import type { MaybePromise } from './MaybePromise';
12+
13+
/**
14+
* Similar to Array.prototype.reduce(), however the reducing callback may return
15+
* a Promise, in which case reduction will continue after each promise resolves.
16+
*
17+
* If the callback does not return a Promise, then this function will also not
18+
* return a Promise.
19+
*/
20+
export default function promiseReduce<T, V>(
21+
values: $ReadOnlyArray<V>,
22+
callback: (T, V) => MaybePromise<T>,
23+
initialValue: T,
24+
): MaybePromise<T> {
25+
return values.reduce((accumulator, value) => {
26+
const promise = getPromise(accumulator);
27+
if (promise) {
28+
return promise.then(resolved => callback(resolved, value));
29+
}
30+
return callback(accumulator, value);
31+
}, initialValue);
32+
}

0 commit comments

Comments
 (0)