Break Out of then promises in Angularjs - angularjs

I am trying to find a way to break out of a promise chain in AngularJS code. The obvious way was to return an object and then check is validity in every "then" function in the chain.
I would like to find a more elegant way of breaking out of a then chain.

In angular, there is the $q service that can be injected in directives, controllers etc, that is a close implentation of Kris Kowal's Q.
So inside of then function instead of returning a value or something else that would be chained to the next "thenable" function, just return a $q.reject('reject reason');
Example:
angular.module('myQmodule',[])
.controller('exController',['$q',function($q){
//here we suppose that we have a promise-like function promiseFunction()
promiseFunction().then(function(result1){
//do the check we want in order to end chain
if (endChainCheck) {
return $q.reject('give a reason');
}
return;
})
.then(function(){
//this will never be entered if we return the rejected $q
})
.catch(function(error){
//this will be entered if we returned the rejected $q with error = 'give a reason'
});
}]);

Related

How to share a model between different controllers through a service in Angular?

I am very new in Angular and I would like to share my problem, just in case someone could help me or guide me to
figure it out the best solution.
How do I share retrieved data from a promise (it can be called only once) between different controller through a service or a factory?
I think I should avoid if it is possible emit or broadcasting more on, either timeouts or similar
Anyway, I am sure it has to exist a pattern to do this properly
service('Service', function(...) {
function ----------(....) {
return myasyncfunction('..............')
.then(function(data) {
return data;
});
}
function getSomething {
return some operation with DATA;
}
return {
getSomething: getSomething
}
}
.controller('CtrlA', function (...) {
Service.getSomething ---> Doesn't work if don't put a delay
}
.controller('CtrlB', function (...) {
}
My code always has dependencies regarding to the fact that I need a promises at the beginning to retrieve the data. I don't
know the proper way to create an architecture in which I don't need a watch, timeout or emit/broadcast & on.
I suspect it is possible to do in another way
The promise should be called only once
Thx in advance
I should also have to use .then on promise(inside your controller) returned by service like -:
Service.getSomething.then(function(result){
//your promise resolve code
},function(error){
//your promise reject code
})

Angular $http.reject vs. $q.reject

I am remember researching this awhile back and coming up empty handed and I still can't find any good info on this - why does there appear to be a $q.reject method but not an $http.reject method?
For example, in real life we might have:
unfollow: function (userId) {
if (!AuthService.isLoggedIn()) {
//$location.url('/login');
window.location.href = '/login';
return $q.reject({error: 'no logged-in user, but non-existent user could still click a follow button?'});
}
else {
return $http({
method: 'PUT',
url: ConfigService.api.baseUrl + '/v1/users/add_unfollow/by_id/' + userId
});
}
}
I would rather uses the relevant $http.reject instead of $q.reject, but that doesn't seem to work.
Since $http returns a promise in one leg of the conditional...the function itself needs to return a promise in the other leg also.
Using $q.reject() is simply a shortcut to return a rejected promise.
Without it, any place that calls unfollow().then() wouldn't have a then() method if a promise wasn't returned
$http would wrap the http calls and return a promise. The promise would rejected if the actual http request is failed, and adding a reject method would not make much sense, rather it should be in the promise.
In your example you would not even need to call the $http service to reject the request.

Chain Angular $http calls properly?

I have been reading about $q and promises for days now and I seem to understand it...somewhat. I have the following situation in practice:
An $http request is made and checks whether a subsequent call can be made.
If the first call fails, return "no data", if it succeeds and says a call can be made, the second call is made, if not - "no data" again. If the second call succeeds, it returns data, if not - "no data". It looks like this (approximately, I simplified for general idea, so don't worry about the minor mistakes here):
return $http.get (something)
.then(function(allowedAccess){
if(allowedAccess){
return $http.get (somethingElse)
.then( function(result){return {data:result} },
function(error){return {data:"n0pe"} }
)
} else {
return {data:"n0pe"}
}
},
function(){ return {data:"n0pe"} });
I was told to use $q here. I don't really understand how or why I would. The $http calls are promises already.
If there is a way to make this cleaner, I don't see it. Just got done re-reading this post on the subject. Essentially, am I missing something / is there a better way to do this?
Edit: Also just re-read a tutorial on chaining promises - it doesn't handle call failures at all. Basically posting this as due diligence.
Edit 2: This is more of an elaborate on the theory I am asking about, excerpt from the first article:
This is a simple example though. It becomes really powerful if your then() callback returns another promise. In that case, the next then() will only be executed once that promise resolves. This pattern can be used for serial HTTP requests, for example (where a request depends on the result of a previous one):
This seems to be talking about chains like this:
asyncFn1(1)
.then(function(data){return asyncFn2(data)})
.then(function(data){return asyncFn3(data)})
So, if I understand correctly this a). Doesn't apply to me because I don't have a 3rd function. b). Would apply to me if I had three functions because while I run an if statement inside the first $http request, and only inside the if statement do I return another promise. So, theoretically, if I had three async functions to chain, I would need to put my if statement inside a promise?
Promises really help with code composition of making async calls. In other words, they allow you to compose your code in a similar manner to how you would compose a synchronous set of calls (with the use of chained .thens) and as if it the sync code was in a try/catch block (with .catch).
So, imagine that your HTTP calls were blocking - the logic you have would look like so:
var allowedAccess, data;
try {
allowedAccess = $http.get(something);
if (allowedAccess){
try{
var result = $http.get(somethingElse);
data = {data: result};
} catch (){
data = {data: "n0pe"};
}
} else {
data = {data: "n0pe"};
}
} catch (){
data = {data: "n0pe"};
}
return data;
You could simplify it a bit:
var allowedAccess, result;
try {
allowedAccess = $http.get(something);
var result;
if (allowedAccess) {
result = $http.get(somethingElse);
} else {
throw;
}
data = {data: result};
} catch () {
data = {data: "n0pe"};
}
return data;
And that would translate to the async version of:
return $http
.get(something)
.then(function(allowedAccess){
if (allowedAccess){
return $http.get(somethingElse);
} else {
return $q.reject(); // this is the "throw;" from above
}
})
.then(function(result){
return {data: result};
})
.catch(function(){
return {data: "n0pe"};
})
At least, this is the reasoning you could apply when composing code with branches and async calls.
I'm not saying that the version I presented is optimal or shorter - it is, however, more DRY because of a single error handling. But just realize that when you do .then(success, error) it is equivalent to try/catch over the previous async operation - this may or may not be needed depending on your specific circumstance.
This is how I would code this sort of problem:
// returns a promise that resolves some endpoint if allowed
function getDataWithAccess(allowed){
return allowed ? $http.get(someEndpoint) : $q.reject();
}
// do something with data
function handleData(data){
// do stuff with your data
}
// main chain
$http.get(accessCredEndpoint)
.then(getDataWithAccess)
.then(handleData)
.catch(function(err){
return { data: "n0pe" };
});
Yes, this is very much like New Dev's answer, however I wanted to make a point of extracting the functions into their own blocks. This makes the overall code much more readable.
$q will help reduce pyramid of calls like this:
async-call1.then(...
aysnc-call2.then(...
This blog post - http://chariotsolutions.com/blog/post/angularjs-corner-using-promises-q-handle-asynchronous-calls/ - offers a clean way of making multiple HTTP requests. Notice the cleaner approach using $q. In case you were hitting a single HTTP endpoint, using your method would have been just fine. I'd say, what you have is fine also; $q might allow greater flexibility in the future.
The blog post describes a service while using $q and the code looks cleaner.
service('asyncService', function($http, $q) {
return {
loadDataFromUrls: function(urls) {
var deferred = $q.defer();
var urlCalls = [];
angular.forEach(urls, function(url) {
urlCalls.push($http.get(url.url));
});
// they may, in fact, all be done, but this
// executes the callbacks in then, once they are
// completely finished.
$q.all(urlCalls)
.then(...
I am a beginner with promises also, so take this with a grain of salt.

$q.defer() creating a resolved promise

looking for some help on an issue that makes me losing my hair ! :)
I have to send a series of call to an API I consume. so I have created a factory with a function like
addItem : function(){
var deferred=$q.defer();
//call to the API
.then(function(response){
deferred.resolve(process(response.data));
}, function(response){
deferred.reject(errorManagement(response.status));
});
}
return deferred.promise;
}
Then I have built an array with code:
for(var i=0; i<nbOfElements; i++) {
arrayOfPromises[i]=Factory.addItem();
}
$q.all(arrayOfPromises).then(..)
My expectation is that the $q.all will resolve only when all calls to the API have been completed.
Unfortunately, it appears that this is not the case, and then I display a partial result, not satisfactory
after some debugging, it appears that the promises returned by the factory all have $$state.status = 1, which seems to be "resolved" state, explaining why the $q.all resolved before I would like.
(link to the values of $$state.status)
Still I find this weird as I have used this $q.defer() a lot, but without $q.all and it always worked fine
Any explaination on this issue and how to solve it would be much welcome :)
Simply return the $http.get() promise will actually work: JSFiddle.
If you want to do some pre-processing in the factory before returning the data (like your process and errorManagement), check demo: JSFiddle.
The $q.all callback function not invoked only after all promises are resolved. It is not because of $q.all.
Your array code has problem: arrayOfPromises[i]=Factory.addItem();. You'd better use push to expand the array.
Maybe I can't solve your problem, just giving suggestions:
You can save a lot of code by returning the promise of your API call instead of creating new promises (this is one of the promise anti-patterns)
addItem: function() {
//call to the API
return $http(something).then(function(response){
return process(response.data);
// if you happen need to reject here, use $q.reject(reason/data);
}, function(response){
return $q.reject(errorManagement(response.status));
});
}
I'm more comfortable on assigning into an array using push
var arrayOfPromises = [];
for(var i=0; i<nbOfElements; i++) {
arrayOfPromises.push(Factory.addItem());
}
$q.all(arrayOfPromises).then(..)
You are not missing the part before .then in the addItem function in your real code, are you?

asynchronous calls inside for loop angularJs

I'm trying to make a call to a function 'submittoServer' which is inside factory 'pService', which makes $http call and then broadcast the data. The call to the 'submittoserver' is happening inside a for loop. Problem here is that I couldn't see the actual call is being made until the last loop, which is sending only the last item, but as you see in the code below I want to update one particular variable after every call, can someone please suggest how can I don't that. I can't do call back here as I've other method that call this same factory function with different inputs.
for (var i = vr.lines.length - 1; i >= 0; i--) {
if (parseInt(vr.lines[i].id) === id && Boolean(vr.lines[i].IsVoided) != true) {
lineId = vr.lines[i].lineID;
pService.submitToServer(actionId, { "IData": id }, ineId)
linesRemoved = linesRemoved + 1;
}
if (linesRemoved === lineqty)
{
updateModel = true;
}
}
The problem here is that your service is a promise to return data. Your function will keep looping and running before the promise is resolved. You need to refactor your loop to take this into account.
Either add a .then(fn(){}) to handle the resolve promise. Gather up all the changed lineIds and submit them all at once and (again) handle the resolved promise with a .then(fn(){}). Lastly, given you next set of code logic, you probably want to do something more like $q.all to wait on all promise(s) to resolve before moving forward (see Wait for all promises to resolve)
Example
before your for loop:
var self=this;
self.linesRemoved = 0; // or init as needed.
Inside your for loop.
pService.submitToServer(actionId,{data}).then(function(resp){
self.linesRemoved++; // shortcut, this does +1 to itself.
});
Why do you have update model? With Angular your data is two-way bound and should just react to it being changed.
Sample $http call with return in a service, this is a promise itself:
return $http.post(url, data, { cache: true });
Use this in a controller like
service.callHttp(data).success(function(resp){
self.linesRemoved++;
}).error(function(resp){});
If you have to wait it might be better to hold all and wait until they are all finished.
var promises =[];
for(){
promises.push(service.callHttp());
}
$q.all(promises).then(function(){
// do work if(self.linesRemoved==lineQty)
// update... You can't evaluate until they are all finished right?
});

Resources