private function executeFields(ObjectType $parentType, $source, $path, $fields)
    {
        $containsPromise = false;
        $finalResults    = [];
        foreach ($fields as $responseName => $fieldNodes) {
            $fieldPath   = $path;
            $fieldPath[] = $responseName;
            $result      = $this->resolveField($parentType, $source, $fieldNodes, $fieldPath);
            if ($result === self::$UNDEFINED) {
                continue;
            }
            if (! $containsPromise && $this->getPromise($result)) {
                $containsPromise = true;
            }
            $finalResults[$responseName] = $result;
        }
        // If there are no promises, we can just return the object
        if (! $containsPromise) {
            return self::fixResultsIfEmptyArray($finalResults);
        }

        // Otherwise, results is a map from field name to the result
        // of resolving that field, which is possibly a promise. Return
        // a promise that will return this same map, but with any
        // promises replaced with the values they resolved to.
        return $this->promiseForAssocArray($finalResults);
    }