Browse Source

[Promise.object] Resolves all promises in nested, cyclic objects

tags/v0.10.0
Muthu Kumar 7 years ago
parent
commit
f1ae5aad20
  1. 113
      index.js

113
index.js

@ -1,67 +1,104 @@
"use strict"; "use strict";
const isObject = obj => obj && typeof obj === 'object' && obj.constructor === Object; /**
* Returns true if x is an object, false otherwise.
* @param {any} x
* @returns {Boolean}
*/
const isObject = x => x &&
typeof x === 'object' &&
x.constructor === Object;
const makeCircular = (obj, path) => { /* A well known Symbol. */
const start = (o, p, i = 1) => { const $SELF = typeof Symbol !== 'undefined' ?
if (i === path.length) return o[p[0]] = obj; Symbol('SELF') :
if (p.length > 0) { '[~~//-- SELF --//~~]';
o[p[0]] = {};
return start(o[p[0]], p.slice(1), i + 1);
} else return obj;
}
return start(obj, path);
};
const findPath = (obj, query) => { /**
return Object.keys(obj).reduce((acc, key) => { * Replaces values that match the query parameter
* with a reference to the parent parameter.
* @param {Object} object Object to make cyclic.
* @param {any} query Query to match against.
* @returns {Object}
*/
const makeCyclic = (object, query) => {
const start = obj => Object.keys(obj).reduce((acc, key) => {
const value = obj[key]; const value = obj[key];
if (value === query) return [...acc, key]; if (value === query) {
if (isObject(value)) return [...acc, ...findPath(value)]; obj[key] = object;
return [...acc, key]
};
if (isObject(value)) return [...acc, ...start(value, query)];
else return acc; else return acc;
}, []); }, []);
return start(object);
}; };
// Resolves array /**
* Promise.map polyfill.
* @param {Array.<any>} arr Array of Promises.
* @param {Function} functor Function to call resolved values.
*/
const PromiseMap = (arr, functor) => const PromiseMap = (arr, functor) =>
Promise.all(arr.map(x => Promise.resolve(x).then(functor))); Promise.all(arr.map(x => Promise.resolve(x).then(functor)));
// Resolves objects /**
* Resolve a flat object's promises.
* @param {Object}
* @returns {Object}
*/
const ResolveObject = obj => const ResolveObject = obj =>
Promise.all( Promise.all(
Object.keys(obj).map(key => Object
Promise.resolve(obj[key]).then(val => obj[key] = val)) .keys(obj)
.map(key =>
Promise
.resolve(obj[key])
.then(val => obj[key] = val))
) )
.then(_ => obj); .then(_ => obj);
// Resolves recrusive deep objects /**
const PromiseObject = parent => { * Recursively resolves deep objects with nested promises.
const ResolveDeepObject = object => * @param {Object} object Object or value to resolve.
* @returns {Object} Resolved object.
*/
const PromiseObject = object => {
let shouldReplaceSelf = false;
const ResolveDeepObject = obj =>
Promise Promise
.resolve(object) .resolve(obj)
.then(obj => { .then(resolvedObject => {
if (Array.isArray(obj)) { if (Array.isArray(resolvedObject)) {
return PromiseMap(obj, obj => ResolveDeepObject(obj)); // Promise and map every item to recursively deep resolve.
} else if (isObject(obj)) { return PromiseMap(resolvedObject, obj => ResolveDeepObject(obj));
} else if (isObject(resolvedObject)) {
return ResolveObject( return ResolveObject(
Object Object
.keys(obj) .keys(resolvedObject)
.reduce((acc, key) => .reduce((acc, key) => {
(obj[key] === parent) ? { if (resolvedObject[key] === object) {
shouldReplaceSelf = true;
return {
...acc, ...acc,
[key]: '[[ SELF ]]', [key]: $SELF, // Replace with resolved object.
} : { }
}
return {
...acc, ...acc,
[key]: ResolveDeepObject(obj[key]), [key]: ResolveDeepObject(resolvedObject[key]),
}
}, {})); }, {}));
} }
return obj; return resolvedObject;
}); });
return ResolveDeepObject(parent) return ResolveDeepObject(object)
.then(obj => { .then(obj => {
const path = findPath(obj, '[[ SELF ]]'); // Replace $SELF with reference to obj
return path.length > 0 ? makeCircular(obj, path) : obj; if(shouldReplaceSelf) makeCyclic(obj, $SELF);
}) return obj;
});
}; };
module.exports = PromiseObject; module.exports = PromiseObject;
Loading…
Cancel
Save