Reputation: 3414
In my node js App, I have a function that checks if you have the permissions:
//queryPermissions is an object that contains all possible permissions:
//each property of queryPermission is an object containing the values to be checked
for (var key in queryPermissions) {
if (queryPermissions.hasOwnProperty(key)) {
promisesArray.push(checkThis(key, req.method));
}
}
Q.all(promisesArray).then(function(response) {
response.forEach(function(value) {
//response is an array with all promises' resolved values
//if all values are true -> access granted
//if one or more values are false -> access denied
}
}
This works good, but if only one of the values returned by checkThis inside the for loop is false, than the result would be access denied; so it is not efficient to continue to check other permissions after the first one that resoves with false; CheckThis returns a promise but sometimes it needs to wait for a query result, sometimes it resolves immediately, it depends.
Is there a way I can break the loop (if it is not finished yet) when the first "checkThis" returns false?
Upvotes: 2
Views: 3166
Reputation: 664610
Not with any native Q
function. You could however write an every
method for promises yourself (loosely based on Q.all
):
Q.every = function every(promises) {
return Q.when(promises, function (promises) {
var countDown = 0;
var deferred = defer();
for (var i=0; i<promises.length; i++) {
var promise = promises[i];
var snapshot;
if (
Q.isPromise(promise) &&
(snapshot = promise.inspect()).state === "fulfilled"
) {
if (!snapshot.value) {
deferred.resolve(false);
return deferred.promise;
}
} else {
++countDown;
q.when(
promise,
function (value) {
if (!value)
deferred.resolve(false);
else if (--countDown === 0)
deferred.resolve(true);
},
deferred.reject,
(function(index) {
return function (progress) {
deferred.notify({ index: index, value: progress });
};
}(i));
);
}
}
if (countDown === 0) {
deferred.resolve(true);
}
return deferred.promise;
});
}
Upvotes: 2
Reputation: 276306
Here is an alternative solution to Bergi's approach - we map false return values to the exceptional condition failures they are and use Q.all
directly:
Your current code does:
for (var key in queryPermissions) {
if (queryPermissions.hasOwnProperty(key)) {
promisesArray.push(checkThis(key, req.method));
}
}
We add an additional step:
for (var key in queryPermissions) {
if (queryPermissions.hasOwnProperty(key)) {
promisesArray.push(checkThis(key, req.method).then(function(val){
if(!val) throw new Error("Invalid Permissions");
return true;
});
}
}
This simple addition would let us use Q directly:
Q.all(promisesArray).catch(function(err){
// one or more authentication errors
}).then(function(){
// everyone validated, all ok user authenticated here
});
This is a more general method - using the exception pipeline for exceptional cases can greatly simplify your code.
Upvotes: 2
Reputation: 3414
Maybe I had a sudden enlightment but was not so hard: just using a global flag:
var alreadyFailed = false;
//queryPermissions is an object that contains all possible permissions:
//each property of queryPermission is an object containing the values to be checked
for (var key in queryPermissions) {
if (alreadyFailed) {
break;
}
if (queryPermissions.hasOwnProperty(key)) {
promisesArray.push(checkThis(key, req.method));
}
}
Then it is sufficient to set alreadyFailed to true inside checkThis when a false result occurs; of course it can happen that the for loop ends before the first resolve to false, but in this case there's no way to do better; but if checkThis resolves with a false before the for loop ends -> it will be stopped
Upvotes: 0
Reputation: 9938
It's not possible. Indeed, Q.all
run parallel promises, so when you want to break, the other promises are still running. So they can't be stopped. One way is to use Q.spread
but therefore the promises are no more parallel :(.
Upvotes: 0