I have a bunch of functions used to provide data to my service. I want to loop through each of them and stop as soon as one of them returns the desired result. If the first one works, thats fine. If it has an exception or data is not valid, I would like to move to the next one and so on.
How may I achieve this? I have the below code:
handleData: function(address) {
var self = this;
return new Promise(function (resolve, reject) {
for (var i = 0; i < self.listAllAvailableProviders.length; ++i) {
var handler = self.listAllAvailableProviders[i];
new handler().getData(address)
.then(function(value) {
Logger.info(value);
resolve(value);
})
.catch(function(err){
Logger.error(err);
})
}
reject("");
});
}
how can I fix it to stop as soon as the first one gets the right data? I have read through the bluebirdjs
documentation to no avail.
EDIT
I put a break
statement after resolve
and I got this:
SyntaxError: Illegal break statement
at Object.exports.runInThisContext (vm.js:53:16)
at Module._compile (module.js:513:28)
at Object.Module._extensions..js (module.js:550:10)
at Module.load (module.js:458:32)
at tryModuleLoad (module.js:417:12)
at Function.Module._load (module.js:409:3)
at Module.require (module.js:468:17)
at require (internal/module.js:20:19)
You are running all your requests in parallel in the for
loop so when you find one that has a value you like, the others have already been started so there's no way to "not" run them. If you want to not run the others once you've found one, you need to not start them in parallel. So, that would lead you to a design pattern where you serialize the requests. Run one, if it doesn't succeed, run the next and so on.
As best I can tell, there isn't a built-in scheme in Bluebird for doing what you're asking. The simplest thing I can think of is to use one of the array processing functions in Bluebird that will serialize the requests one after the other such as Promise.mapSeries()
and then use a rejection to abort the processing when you found a good value.
handleData: function(address) {
return Promise.mapSeries(this.listAllAvailableProviders, function(handler) {
return new handler().getData(address).then(function(value) {
// the first success we get, we will throw with
// the returned value in order to stop the .mapSeries progression
throw value;
}, function(err) {
// log the error, but don't let the rejection propagate so other handlers are called
Logger.error(err);
})
}).then(function() {
// nothing succeeded here, turn it into an overall rejection
throw new Error("No getData() handlers succeeded");
}, function(val) {
// reject here means we got a good value so turn it into a resolved value
return val;
})
}
// usage
obj.handleData().then(function(val) {
// got value here
}).catch(function(err) {
// no values here
});
Curiously enough, it seems to be less code and perhaps a bit simpler if you just iterate the handlers yourself:
handleData: function(address) {
var index = 0;
var handlers = this.listAllAvailableProviders;
var handlerCnt = handlers.length;
function next() {
if (index < handlerCnt) {
var handler = handlers[index++];
return new handler().getData(address).catch(next);
} else {
return Promise.reject(new Error("No handler found for address"));
}
}
return next();
}
if promises are not a hard constraint caolan/async#eachSeries or similar might help. Something like...
// var Promise = require(?)
// var async = require("async")
handleData: asyncProviderFinder
...
function asyncProviderFinder(address){
var self = this;
return new Promise(function(resolve, reject){
async.eachSeries(
self.listAllAvailableProviders,
function iterate(provider, next){
var handler = provider;
new handler().getData(address)
.then( function(value){
Logger.info(value);
next("abort"); // callback any error to abort future iterations
return resolve(value);
})
.catch( function (err){
Logger.error(err);
next();
});
},
function callback(err, firstProvider){
if ((firstProvider === undefined) && !err ){ reject(""); }
}
);
});
}