Skip to content

Commit

Permalink
Execute serially supporting sync execution.
Browse files Browse the repository at this point in the history
Refactors executeFieldsSerially to return MaybePromise

Fixes #1195
  • Loading branch information
leebyron committed Jan 10, 2018
1 parent 358df97 commit 51e44db
Show file tree
Hide file tree
Showing 3 changed files with 84 additions and 40 deletions.
68 changes: 28 additions & 40 deletions src/execution/execute.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,12 @@

import { forEach, isCollection } from 'iterall';
import { GraphQLError, locatedError } from '../error';
import getPromise from '../jsutils/getPromise';
import invariant from '../jsutils/invariant';
import isInvalid from '../jsutils/isInvalid';
import isNullish from '../jsutils/isNullish';
import memoize3 from '../jsutils/memoize3';
import promiseReduce from '../jsutils/promiseReduce';
import type { ObjMap } from '../jsutils/ObjMap';
import type { MaybePromise } from '../jsutils/MaybePromise';

Expand Down Expand Up @@ -465,33 +467,33 @@ function executeFieldsSerially(
sourceValue: mixed,
path: ResponsePath | void,
fields: ObjMap<Array<FieldNode>>,
): Promise<ObjMap<mixed>> {
return Object.keys(fields).reduce(
(prevPromise, responseName) =>
prevPromise.then(results => {
const fieldNodes = fields[responseName];
const fieldPath = addPath(path, responseName);
const result = resolveField(
exeContext,
parentType,
sourceValue,
fieldNodes,
fieldPath,
);
if (result === undefined) {
return results;
}
const promise = getPromise(result);
if (promise) {
return promise.then(resolvedResult => {
results[responseName] = resolvedResult;
return results;
});
}
results[responseName] = result;
): MaybePromise<ObjMap<mixed>> {
return promiseReduce(
Object.keys(fields),
(results, responseName) => {
const fieldNodes = fields[responseName];
const fieldPath = addPath(path, responseName);
const result = resolveField(
exeContext,
parentType,
sourceValue,
fieldNodes,
fieldPath,
);
if (result === undefined) {
return results;
}),
Promise.resolve({}),
}
const promise = getPromise(result);
if (promise) {
return promise.then(resolvedResult => {
results[responseName] = resolvedResult;
return results;
});
}
results[responseName] = result;
return results;
},
Object.create(null),
);
}

Expand Down Expand Up @@ -1346,20 +1348,6 @@ export const defaultFieldResolver: GraphQLFieldResolver<any, *> = function(
}
};

/**
* Only returns the value if it acts like a Promise, i.e. has a "then" function,
* otherwise returns void.
*/
function getPromise<T>(value: Promise<T> | mixed): Promise<T> | void {
if (
typeof value === 'object' &&
value !== null &&
typeof value.then === 'function'
) {
return (value: any);
}
}

/**
* This method looks up the field on the given type defintion.
* It has special casing for the two introspection fields, __schema
Expand Down
24 changes: 24 additions & 0 deletions src/jsutils/getPromise.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
/**
* Copyright (c) 2015-present, Facebook, Inc.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @flow
*/

/**
* Only returns the value if it acts like a Promise, i.e. has a "then" function,
* otherwise returns void.
*/
export default function getPromise<T>(
value: Promise<T> | mixed,
): Promise<T> | void {
if (
typeof value === 'object' &&
value !== null &&
typeof value.then === 'function'
) {
return (value: any);
}
}
32 changes: 32 additions & 0 deletions src/jsutils/promiseReduce.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
/**
* Copyright (c) 2015-present, Facebook, Inc.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @flow
*/

import getPromise from './getPromise';
import type { MaybePromise } from './MaybePromise';

/**
* Similar to Array.prototype.reduce(), however the reducing callback may return
* a Promise, in which case reduction will continue after each promise resolves.
*
* If the callback does not return a Promise, then this function will also not
* return a Promise.
*/
export default function promiseReduce<T, V>(
values: $ReadOnlyArray<V>,
callback: (T, V) => MaybePromise<T>,
initialValue: T,
): MaybePromise<T> {
return values.reduce((accumulator, value) => {
const promise = getPromise(accumulator);
if (promise) {
return promise.then(resolved => callback(resolved, value));
}
return callback(accumulator, value);
}, initialValue);
}

0 comments on commit 51e44db

Please sign in to comment.