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?
Related
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
})
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.
I'm facing this strange issue with the $http service: every request made to the API provided on the company that I work, wrapped in a $q.all(), return the same response.
var promises = [
$httpPromiseA(),
$httpPromiseB(),
$httpPromiseC()
];
$q.all(promises)
.then(function (response) {
$log.log(response);
});
// $log.log returns [expectedObjectFromA, expectedObjectFromA, expectedObjectFromA]
This occurs randomly: the expectedObjectFromA can be expectedObjectFromB or expectedObjectFromC. The fact is that all of the three objects return the same, or two of one and one of other, etc.
If I chain one after other, in a linear manner, they all work perfectly, but takes more time to acomplish the task, of course.
var def = $q.deferred();
var resolvedData = [];
$httpPromiseA()
.then(function (response) {
$log.log(response);
resolvedData.push(reponse);
return $httpPromiseB();
})
.then(function (response) {
$log.log(response);
resolvedData.push(reponse);
return $httpPromiseC();
})
.then(function (response) {
$log.log(response);
resolvedData.push(reponse);
return def.resolve(resolvedData);
});
return def.promise();
// $log.log returns [expectedObjectFromA],
// [expectedObjectFromB] and [expectedObjectFromC]
// as expected. ResolvedData brings [expectedObjectFromA,
// expectedObjectFromB, expectedObjectFromC].
Can you give me some directions on what may be happening here?
I'm using the core implementations of $http and $q. I've tried one $q.allSettled, applied as $decorator, based on the API resource of Kris Kowalk's Q, but didn't succeed too.
Thanks.
EDIT 1:
I cannot pass the arguments to the functions separately because it's a wrapper function thats call the $http service, here in my app. The wrapper function expects a String as the first argument, and an Object as the second. The wrapper function returns the $http calls.
EDIT 2:
This Plunker calls 2 concurrent requests, one to the Instagram API and the other to the Flickr API. The problem don't occur is this Plunker. I really don't know how to deal with this trouble. This is REALLY annoying.
In $q.all you'll want to pass in an array of promises, instead executing the function. $httpPromiseA.$promise instead of $httpPromiseA() and so on.
The problem was on the server side, which was having problems with simultaneous requests, answering all made together with the same response.
Thanks for all support and attention.
I'm learning about Angular JS and on the moment I'm trying to understand about promises and async programming and I have this doubt about $q.defer(). My point is the following: usually when people work with promises they do something like that, considering that $q is already available
function someAsyncFunction() {
var deferred = $q.defer();
/* Do things and if everything goes fine return deferred.resolve(result)
otherwise returns deferred.reject()
*/
return deferred.promise;
}
What is this really doing? When we do var deferred = $q.defer() it imediately switches all the execution of that function to another thread and return the promise being a reference to the results of this operation that is still performing there?
Is this the way we should think about when creating async methods?
With $q u run functions asynchronously.
Deferred objects signals that something, some task is done.
var defer = $q.defer(); // we create deferred object, which will finish later.
defer.promise // we get access to result of the deferred task
.then( // .then() calls success or error callback
function(param) {
alert("i something promised " + param);
return "something";
}); // u can use 1 or more .then calls in row
defer.resolve("call"); //returns promise
Here example:
http://jsfiddle.net/nalyvajko/HB7LU/29048/
Angular's $q service is based on the Javascript library Q. You can read more about it in the Q documentation, or read the code in the github repo. I think this part snipped from the introduction to the documentation explains it best:
If a function cannot return a value or throw an exception without
blocking, it can return a promise instead. A promise is an object that
represents the return value or the thrown exception that the function
may eventually provide. A promise can also be used as a proxy for a
remote object to overcome latency.
I am using AngularJS Services in my application to retrieve data from the backend, and I would like to make a loading mask, so the loading mask will start just before sending the request. but how can I know when the request ends?
For example I defined my servive as:
angular.module('myServices', ['ngResource'])
.factory('Clients', function ($resource) {
return $resource('getclients');
})
.factory('ClientsDetails', function ($resource) {
return $resource('getclient/:cltId');
})
So I use them in my controller as:
$scope.list = Clients.query();
and
$scope.datails = ClientsDetails.get({
date:$scope.selectedId
});
So the question would be, how to know when the query and get requests ends?
Edit:
As a side note in this question I've been using using angularjs 1.0.7
In AngularJS 1.2 automatic unwrapping of promises is no longer supported unless you turn on a special feature for it (and no telling for how long that will be available).
So that means if you write a line like this:
$scope.someVariable = $http.get("some url");
When you try to use someVariable in your view code (for example, "{{ someVariable }}") it won't work anymore. Instead attach functions to the promise you get back from the get() function like dawuut showed and perform your scope assignment within the success function:
$http.get("some url").then(function successFunction(result) {
$scope.someVariable = result;
console.log(result);
});
I know you probably have your $http.get() wrapped inside of a service or factory of some sort, but you've probably been passing the promise you got from using $http out of the functions on that wrapper so this applies just the same there.
My old blog post on AngularJS promises is fairly popular, it's just not yet updated with the info that you can't do direct assignment of promises to $scope anymore and expect it to work well for you: http://johnmunsch.com/2013/07/17/angularjs-services-and-promises/
You can use promises to manage it, something like :
Clients.query().then(function (res) {
// Content loaded
console.log(res);
}, function (err) {
// Error
console.log(err);
});
Another way (much robust and 'best practice') is to make Angular intercepting your requests automatically by using interceptor (see doc here : http://docs.angularjs.org/api/ng.$http).
This can help too : Showing Spinner GIF during $http request in angular
As left in a comment by Pointy I solved my problem giving a second parameter to the get function as following:
$scope.datails = ClientsDetails.get({
date:$scope.selectedId
}, function(){
// do my stuff here
});