I have the following q.all calling to resolve two promises. I checked all the posts and tried all other ways of implementation q.all and its the same case
var xyzdeffered = $q.defer();
service1.getServiceDetail1($routeParams.id).then(function(promise) {
xyzdeffered.resolve(promise);
});
var abcdeffered = $q.defer();
service2.getServiceDetail2($routeParams.id).then(function(promise) {
abcdeffered.resolve(promise);
});
$q.all([ xyzdeffered, abcdeffered ]).then(function(data) {
$scope.variable = data;
});
I am expecting the variable in q.all should get populated only after the first two promises are resolved. But unfortunately the service call itself doesn't get returned with data and the control moves on to the q.all. I find this weird because as per documentation the q.all is called only when your promises are returned with 200 response and are resolved.
I checked analysing the network calls and also put some alert to see the sequence of the code and find that the q.all alert is the first alert to be popped up and then the other promises are resolved.
Its really making me mad as why a simple implementation of q.all doesnt work..
Any help will be appreciated.
Why not directly call $q.all on first promise ?
$q.all([
service1.getServiceDetail1($routeParams.id),
service2.getServiceDetail2($routeParams.id)
]).then(function(data) {
//Array of result [resultOfgetServiceDetails1, resultOfgetServiceDetails2]
$scope.variable = data;
});
You need to reference the promise on the $q.defer() return object:
$q.all([ xyzdeffered.promise, abcdeffered.promise ])
For example you have you run multiple sq-lite queries synchronously just pass the array of queries(you can pass anything) as args into the call of this method.
function methodThatChainsPromises(args,tx) {
var deferred = $q.defer();
var chain = args.map(function(arg) {
var innerDeferred = $q.defer();
tx.executeSql(arg,[], function(){
console.log("Success Query");
innerDeferred.resolve(true);
}, function(){
console.log("Error Query");
innerDeferred.reject();
});
return innerDeferred.promise;
});
$q.all(chain).then(
function(results) {
deferred.resolve(true)
console.log("deffered resollve"+JSON.stringify(results));
}, function(errors) {
deferred.reject(errors);
console.log("deffered rejected");
});
return deferred.promise;
}
Related
I found myself in a little callback hell and found it difficult to test.
I hope maybe some of you guys could help me to figure this out
I have 2 services and my problem is with the fooService.getModel function
app.service('fooService', function($q, barService){
return {
getModel: function(){
var deferred = $q.defer();
barService.get().then(function(response){
//my bareService mock allow me to reach up to this point
var result = {};
//some processing logic (if, else, etc)
console.log('resolve result') // I totally see this log
deferred.resolve(result); //this is what i want to test
});
return deferred.promise;
},
process: function(){
this.getModel().then(function(result){
if(result.success){
barService.post().then(function(){
//whatever i dont care
})
}
});
}
}
});
and barService which contain 2 methods(get and store) basically $http calls.
for bar service i dont have problems testing because logic is isolated
I'm just faking the $httpBackend calls, with different responses.
the problem that i found when i want to verify the result object being returned on getModel function.
my test is something like
it('should test the returning model', inject(function(fooService){
barService.get.and.returnValue({then: function(successCallback){
successCallback({status:204, data: {}});
}});
var result;
fooService.getModel().then(function(response){
console.log('unit test then') // never gets in here
result = response;
});
//or anything related to the result object
expect(result).toEqual(mockedModel);
expect(result.success).toBeTruthy()
})
what i have seen is that if my getModel function will execute the http call itself, i can evaluate the result in my unit test.
something like
getModel: function () {
var deferred = $q.defer();
$http.get(url).then(function (response) {
deferred.resolve(response);
})
.catch(function (response) {
deferred.reject(response);
});
return deferred.promise;
}
i would definetly love to extract the functionality of my barService to my fooService, but this is very likely to be rehusable that why i extracted to a separate service.
do you have any idea how can i fix my unit test to evaluate the result response?
i hope its clear
thanks for the help guys
nevermind found that i even it is a service, i need to do $scope.digest in my unit test.
thanks!
This might be a nooby question but I still haven't been able to get my head around promises and specifically how to write code with them. (I've read several articles but most of them are abstract and I simply haven't written enough to have a clear picture)
I've got an AngujlarJS application that gets data through a http request to another server which sends a promise at first. I've been able to retrieve the response from the promise and use it in my app. However because my code is poorly written. It executes other code before the promise is resolved leading to problems. It starts loading the page before it has the data.
what i have is:
var userTotals = *http request which returns a promise
$scope.data = userTotals.$object
//code that does someting with $scope.data
What i need is (I think)
var userTotals = *http request which returns a promise
$scope.data = userTotals.$object.
beforethisresolves(function{
show fancy loading icon or something })
.whenthis resolves(function{
//code that does someting with $scope.data
}
however I can't get the syntax correct.
This is what it looks like in general:
var promise = $http.post('/url');
console.log('Request started');
promise.then(function(result) {
console.log('Success');
console.log(result);
}, function() {
console.log('Failure');
});
In fact, $q AngularJS documentation helped me a good deal to get my head around promises concept.
Hope this helps!
var successCallback = function(){//...};
var errorCallback = function(){//...};
$http
.post('url', data)
.success(successCallback)
.error(errorCallback);
//OR
$http
.post('url', data)
.then(successCallback, errorCallback);
Assuming that you're using Bootstrap modal you can do the following:
function openModalWithProgressIndicator(deferred) {
const progressModal = $uibModal.open({
templateUrl: 'templates/modals/progress.html',
backdrop: 'static'
});
return deferred.then(function (value) {
return value;
}).finally(function () {
progressModal.close();
});
}
The deferred argument passed to this function is a promise. That said you can now do the following:
const deferred = $http.post('http://somewhere/data', {foo: 'bar'});
openModalWithProgressIndicator(deferred)
.then(function (httpResponse) {
// do sth with httpResponse.data
}).catch(function (error) {
// do sth with error
});
So the main point to note is the finally callback that's always executed.
I'm having some trouble managing a for loop with multiple promises. I've read that I should use $q.all, but I havent had any success with it thus far. I need this function to make sure all of the async calls inside the for loop have occurred.
refreshFeed: function(){
var defer = $q.defer();
var promises = []; // array of promises
for (var x = 0; x < userdata.length; x++){
this.getLatest(x).then(function(){
promises.push(x);
});
}
console.log(userdata); // this is updated successfully by getLatest(x)
$q.all(promises).then(function(){
defer.resolve();
})
return defer;
}
It successfully prints the console.log but then I get the console error "undefined is not a function" at the line that initially calls refreshFeed().
Any help would be appreciated. Thank you!
Edit:
getLatest: function(x){
var deferred = $q.defer();
var promise = twitterService.getLatestTweets(userdata[x].searchterm, userdata[x].sources).then(function(data) {
userdata[x].tweets = data;
console.log(userdata[x].tweets);
deferred.resolve();
});
return deferred.promise;
}
Update: I used PSL's answer and toyed around with getLatest and I finally got it to work. I just needed to replace the userdata[x]'s with the function parameter. Thanks #PSL
You would need to pass an array of promises and even if you are using deferred pattern you are not resolving anything. Assuming getLatest returns a promise you could just do:-
refreshFeed: function(){
return $q.all(userdata.map(this.getLatest));
}
Array.map
Promises are one of those things that I can understand while I'm looking at them, but then the comprehension vanishes when I look away.
I need to grab two pieces of data asynchronously, then combine the result and store it in a cookie. I think I could implement a vanilla promise chain without much difficulty. That is,
loadData1().then(loadData2).then(setCookie);
However, I don't need to wait for one request to finish before making the other. How can I do something like
(loadData1(); loadData2();).then(setCookie);
Here's a quick example using $q.all:
$q.all([
(function () {
var d = $q.defer();
API.get({}, function (data) {
d.resolve(data);
});
return d.promise;
})(),
(function () {
var d = $q.defer();
API.getMore({}, function (data) {
d.resolve(data);
});
return d.promise;
})()
]).then(function(responses) {
//responses contains an array of all your data responses, in the order they were chained to $q
});
I have a wcf service method that gets some data and I call it using Microsoft Ajax library.
To share this data I create a dataService, and many controllers use this service.
I want every controller to get the same data after first call of getData is done, unless somebody need to refresh data and set forceRefresh to true.
My code fails because with the initialize of application 3 controler call dataService.getData and for all there start a new request. How can I make wait dataService.getData calls until the first one is finished and get same result for other subsequent ones..
angular.module('app', []).factory('dataService', function ($q) {
var data= [];
var getData= function (forceRefresh) {
console.log('getFolders called: ', reports.length);
var deferred = $q.defer();
if (forceRefresh || data.length < 1) {
WcfService.GetData(function(result) {
data= result;
deferred.resolve(data);
}, function(ex) { console.log(ex); });
} else {
deferred.resolve(reports);
}
return deferred.promise;
};
return {
getData: getData
};
});
One way would be to cache the promise, rather than the data, so it gets cached when it is created and not when the data arrives. After all, this sounds like your use case.
angular.module('app', []).factory('dataService', function ($q) {
var deferred = null;
var getData= function (forceRefresh) {
console.log('getFolders called: ', reports.length);
if(!forceRefresh && deferred) return deferred.promise;
deferred = $q.defer();
WcfService.GetData(
function(result) { deferred.resolve(data); }, // I'd promisify at a
function(ex){ deferred.reject(ex); } // higher level probably
);
return deferred.promise;
};
return {
getData: getData
};
});
how about setting a global flag in the $rootscope when a controller is querying for data, which can be checked before fetching the data by all the controllers, hence, avoiding redundant calls. The same flag can be put down when any of the controller has the promise fulfilled and data has been fetched, which can then be shared amongst all the controllers.
I found exactly what I search for
Promise queue for AngularJS services
http://inspectorit.com/tips-tricks/promise-queue-for-angularjs-services/