Tim Hardy Tim Hardy - 4 months ago 8
Node.js Question

How do I mix all this logic with javascript Promises?

I'm using bluebird in Node, and I'm still pretty new to using Promises, especially when things start getting beyond the basics.

Here's a function I need to construct using Promises, and I'm struggling to figure out the best way to set it up. At a high level, this function will take a model object, and return it, converting any query properties to their result sets. For example, a property can have a value of "query(top5Products)", and we'll need to lookup that named query and replace the value with the results of that query. Properties can also be an actual string-based query (using RQL, e.g. "eq(contentType,products)&&limit(5,0)") This converted model object will then be used to bind against a template.

Here's my pseudo-coded function, currently synchronous except for the calls to existing promise-returning services...

function resolveQueryPropertiesOnModel(model) {
for (let property in model) {
if (model.hasOwnProperty(property)) {
let queryName = this.getNameOfNamedQuery(model[property]); // will return undefined if the property is not a named query
if (queryName) {
// this property is a named query, so get it from the database
this.getByName(queryName)
.then((queryObject) => {
// if queryObject has a results propery, that's the cached resultset - use it
if (queryObject && queryObject.results) {
model[property] = queryObject.results;
}
else {
// need to resolve the query to get the results
this.resolve(queryObject.query)
.then((queryResults) => {
model[property] = queryResults;
});
}

};
}
else if (this.isQuery(model[property]) { // check to see if this property is an actual query
// resolve the query to get the results
this.resolve(model[property])
.then((queryResults) => {
model[property] = queryResults;
});
}
}
}
// return some sort of promise that will eventually become the converted model,
// with all query properties converted to their resultsets
return ???;
}


I'm still very rusty when it comes to taking loops with logic and some pre-existing promises and mashing them all together.

Any help will be appreciated.

Answer

This is how I would solve it.

  • a q.all() will be resolved if all of the promises are resolved. each promise is one property in the model that is processed.
  • for each property (I'd use a library like lodash and _.reduce, but you can use the hasOwnProperty if you like). anyway, foreach property, resolveModelProperty function returns a promise that decides the fate of the property, if there is a query name, get it, if not and there is a query, resolve it, if not, don't change the property.
  • to helper functions, resolveByName and resolveQuery will handle the case of cached and uncached queries.

function resolveQueryPropertiesOnModel(model) {
   const promises = [],
        resolveQuery = toBeResolved => this.resolve(toBeResolved),
        resolveByName = queryName => this.getByName(queryName)
		  .then(queryObject => queryObject && queryObject.results 
                   ? queryObject.results : resolveQuery(queryObject.query)),
        resolveModelProperty = (modelProperty) => {
	        const queryName = this.getNameOfNamedQuery(modelProperty);
	        return queryName ? resolveByName(queryName) : 
                  this.isQuery(modelProperty) ? resolveQuery(modelProperty):
                          modelProperty;
        };

	for(let property in model)
		if( model.hasOwnProperty(property)
			promises.push(resolveModelProperty(model[property])
                          .then(result=> model[property]=result));
	return q.all(promises);
}