I am trying to make few ajax call inside a loop. I want to print a message only when my last ajax is successful. Any suggestions what I am doing wrong here.
$scope.defer = $q.defer();
$scope.promises = [];
$scope.array = [1,2,3,4,5,6,7,8,9,10];
$scope.makecall = function(){
angular.forEach($scope.array, function(index){
$timeout(function() {
var promise = $http({
url : 'https://jsonplaceholder.typicode.com',
method: 'GET',
}).
success(function(data){
});
$scope.promises.push(promise);
}, index * 2000);
});
return $scope.defer.promise;
}
$scope.makecall();
$q.all($scope.promises).then(function(){
console.log('over!');
});
I think you can check the SO
Wait for all promises to resolve
$q.all([one.promise, two.promise, three.promise]).then(function() {
console.log("ALL INITIAL PROMISES RESOLVED");
});
var onechain = one.promise.then(success).then(success),
twochain = two.promise.then(success),
threechain = three.promise.then(success).then(success).then(success);
$q.all([onechain, twochain, threechain]).then(function() {
console.log("ALL PROMISES RESOLVED");
});
you may also check
function outerFunction() {
var defer = $q.defer();
var promises = [];
function lastTask(){
writeSome('finish').then( function(){
defer.resolve();
});
}
angular.forEach( $scope.testArray, function(value){
promises.push(writeSome(value));
});
$q.all(promises).then(lastTask);
return defer.promise;
}
Related
I have an angularJS app that utilizes two services to retrieve data from a DB.
session.js
angular.module('RiskAssessment').service('session', ['dbInterface', function(dbInterface) {
this.getBatches = function () {
if (!this.batches) {
console.log("Retrieved Batches");
var that = this;
return this.pullBatches().then(function (data) {
that.batches = data; //Is this EVEN possible?
});
} else {
console.log("Didn't retrieve Batches");
}
return this.batches;
};
this.pullBatches = function () {
return dbInterface.pullBatches(this.getUserId());
};}]);
dbInterface.js
pullBatches: function(userId){
return $http.post('db_queries/get_batches.php', userId)
.then(function (response) {
console.log("get_batches.php POST Result: ", response.data);
return response.data;
})
.catch(function (response) {
console.log("Error post");
});
}
I want to able to get this.batches via getBatches() if it has already been retrieved and set. Otherwise, I'd like to use pullBatches() to retrieve and set this.batches. The answer is probably some mix of promises, but I am struggling with this.
Thank you for reading!
EDIT ::
How do I set this.batches within a .then() of my call to .pullBatches()?
this.getBatches = function(){
if(!this.batches) {
console.log("Retrieved Batches");
var deferred = $q.defer();
deferred = this.pullBatches().then(function(data){
//this.batches = data; <---------------------------- HERE
});
return deferred.promise;
}else{
console.log("Didn't retrieve Batches");
}
return this.batches;
};
EDIT 2 ::
With great help from #Jahirul_Islam_Bhuiyan I fixed my issue.
this.getBatches = function(){
var deferred = $q.defer();
if(!this.batches){
console.log("Retrieved Batches");
dbInterface.pullBatches(this.getUserId()).then(function(payload){
deferred.resolve(payload.data);
service.setBatches(payload.data);
});
}else{
console.log("Didn't retrieve Batches");
deferred.resolve(this.batches);
}
return deferred.promise;
};
this.setBatches = function(batches){
this.batches = batches;
};
In Controller...
session.getBatches().then(function(data){
//console.log("getBatches.then() : " + JSON.stringify(data));
$scope.batches = data;
});
I now have a much greater understanding of promises!
Try following
this.getBatches = function(){
var deferred = $q.defer();
if(!this.batches) {
console.log("Retrieved Batches");
this.pullBatches().then(function(data){
deferred.resolve(data);
});
}else{
console.log("Didn't retrieve Batches");
deferred.resolve(this.batches);
}
var promises = [
deferred.promise
];
var promise = $q.all(promises);
return promise;
};
hope this help
Have this scenario when I make a request via $http in the first service, and then I want to manipulate the data in other service. For some reassons my returned object is empty. Any advice ?
.factory('widgetRestService',['$http','$log',function($http,$log){
var serviceInstance = {};
serviceInstance.getInfo = function(){
var request = $http({method: 'GET', url: '/rest/widgets/getListInfoDashboards'})
.then(function(success){
serviceInstance.widgets = success.data;
$log.debug('serviceInstance.widgets SUCCESS',serviceInstance.widgets);
},function(error){
$log.debug('Error ', error);
$log.debug('serviceInstance.widgets ERROR',serviceInstance.widgets);
});
return request;
};
serviceInstance.getAllWidgets = function () {
if (serviceInstance.widgets) {
return serviceInstance.widgets;
} else {
return [];
}
};
return serviceInstance;
}])
.factory('OtherService',['widgetRestService','$log', function(widgetRestService, $log){
widgetRestService.getInfo();
// and now I want to return widgetRestService.widgets [{things},{things},{things}]
return widgetRestService.getAllWidgets(); // returns []
}])
Result: [ ]
You must wait for your HTTP request to complete before trying to access the data that it returns. You can do this by accessing getAllWidgets within a then attached to the Promise returned by getInfo.
.factory('OtherService', ['widgetRestService','$log', function(widgetRestService, $log) {
return widgetRestService.getInfo().then(function () {
return widgetRestService.getAllWidgets();
});
}])
Consume OtherService somewhere:
OtherService.then(function (widgets) {
// do something with `widgets`...
});
It seems you have to return promise instead of response to get the data from another service.
serviceInstance.getInfo = function(){
var deferred = $q.defer();
$http({method: 'GET', url: '/rest/widgets/getListInfoDashboards'})
.then(function(success){
deferred.resolve(success.data);
},function(error){
$log.debug('Error ', error);
$log.debug('serviceInstance.widgets ERROR',serviceInstance.widgets);
});
return deferred.promise;
};
widgetRestService.getInfo() is returning a promise, so you have to wait for its resolution:
.factory("OtherService", ["widgetRestService", "$log", "$q"
function (widgetRestService, $log, $q) {
var deferred = $q.defer();
widgetRestService.getInfo().then(function () {
// only here, you're sure `serviceInstance.widgets` has been set (unless an error occurred when requesting the api)
deferred.resolve(widgetRestService.getAllWidgets());
});
return deferred.promise;
}])
Then, e.g. in a controller:
.controller("SomeController", ["$scope", "OtherService"
function ($scope, OtherService) {
OtherService.then(function (allWidgets) {
$scope.allWidgets = allWidgets;
});
}])
On my app, I need to recover data (json) by making multiples validations using http requests before all my app starts. So my problem is that I'm using angular.run() to make all the http requests and resolving all of the validations with promises.
The problem is, not all of my promises are executed before my app is started.
part of my code is:
appModule.run(configRun);
configRun.$inject = [
'$http', '$rootScope', 'gettextCatalog', 'ipLoadDataService',
'webStorageService', 'ipDataSetParserService'];
function configRun($http, $rootScope, gettextCatalog, ipLoadDataSrv, webStrSrv, dataSetParser) {
webStrSrv.clear();
ipLoadDataSrv.getHeadDataSet2()
.then(function (responseHead) {
if (ipLoadDataSrv.updatedDataSet2(responseHead.headers["last-modified"])) {
//save into localstorage
webStrSrv.clear();
webStrSrv.setItem("last-modified", { date: responseHead.headers["last-modified"] });
ipLoadDataSrv.getDataSet2()
.then(function (responseData) {
$rootScope.cabecera = responseData;
})
}
})
}
// LoadDataService
appModule.factory('ipLoadDataService', loadDataService);
loadDataService.$inject = ['$http',
'$q',
'webStorageService',
'myPrjEnvironment',
'ipDataSetParserService'];
function loadDataService($http, $q, webStoreService, myPrj, dataSetParser) {
var eventMap = [];
var ip_loadDataService = {
getHeadDataSet2: getHeadDataSet2,
requestDataSet: requestDataSet,
updatedDataSet2: updatedDataSet2,
getDataSet2: getDataSet2
};
return ip_loadDataService;
function getHeadDataSet2() {
/*HEAD*/
var deferred = $q.defer();
$http.head(myPrj.URL_DATA)
.success(function (data, status, headers, config) {
var response = [];
response.data = data;
response.headers = headers();
deferred.resolve(response);
//return deferred.promise;
}).error(function (data, status) {
deferred.reject(data);
});
return deferred.promise;
}
function getDataSet2() {
return xhr('get', [myPrj.URL_DATA]);
}
function updatedDataSet2(last_date_modified) {
//var self = this;
var dateOnWebStore = webStoreService.getItem("last-modified");
if (dateOnWebStore === null || Date.parse(dateOnWebStore.date) < Date.parse(last_date_modified))
return true;
return false;
}
function xhr(type, config) {
if (!config && angular.isArray(type)) {
config = type;
type = 'get';
}
var deferred = $q.defer();
$http[type].apply($http, config)
.success(function (data, status, headers, config) {
var response = [];
response.data = data;
response.headers = headers();
deferred.resolve(response);
})
.error(function (error) {
deferred.reject(error);
});
return deferred.promise;
}
}
Answering the question in your second post, maybe you better edit your original post with the new issue you encountered.
If what you are looking for is a way to activate a state (home.myPrjMain in your case) you can do this in various ways:
Using JS - use $state.go(). See - $State documentation
Using directive - use the ui-sref directive with the name of the required state. See - ui-sref documentation
Using regular html href (Navigate to url) - with the full address of the state you need. In your case, "/main".
I hope this was helpful
Have the UI start in an initial loading state then use ui-router to wait for the various pieces to resolve before going to the initial state.
Here is a fiddle showing how it works. Fiddle
I did two parts, one with a single fake service call using timeout and a second with a chained set of calls,.
this.slowServiceCall = function(input, delay) {
var deferred = $q.defer();
var workFinished = function () {
deferred.resolve(input);
};
$timeout(workFinished, delay);
return deferred.promise;
};
this.slowChainedServiceCall = function(input, delay) {
var deferred = $q.defer();
var workFinished = function () {
deferred.resolve(input);
};
$timeout(workFinished, delay);
var promiseChain = deferred.promise.then(function(result) {
var deferred2 = $q.defer();
$timeout(function(){
deferred2.resolve(result + ' Second Piece');
},100);
return deferred2.promise;
});
return promiseChain;
};
i need some data when change state using ui-router
that data depend from each other, so i have to use chaining promises
svc.getData1AndData2 = function(){
var defer = $q.defer();
$q.all([svc.getDatas1(),svc.getDatas2()]).then(function(values) {
$rootScope.datas2 = values[1];
$rootScope.datas1 = values[0];
defer.resolve(values);
}, function(error) {
console.log(error);
defer.reject(error);
});
return defer.promise;
svc.getData3Sources = function(){
svc.getData1AndData2().then(function(value){
return svc.getSources(24);
})
};
svc.getSources=function(id){
var defer = $q.defer();
Services.query({Id:id}, function(data){
defer.resolve(data);
};
};
And my state is
.state('secure.list', {
url: "/list",
templateUrl: "views/list.html",
controller: 'ListCtrl',
resolve: {
InitData: function (InitFactory) {
return InitFactory.getData3Sources();
}
}
})
it return undefined. who know why?
svc.getData3Sources doesn't return anything ..
try ...
svc.getData1AndData2 = function(){
// no need to use $defer here
var promise = $q.all([svc.getDatas1(),svc.getDatas2()]).then(function(values) {
$rootScope.datas2 = values[1];
$rootScope.datas1 = values[0];
return values;
}, function(error) {
console.log(error);
defer.reject(error);
});
return promise;
}; // this line was missing
svc.getData3Sources = function(){
var promise = svc.getData1AndData2().then(function(values){
// changed value to values in the line above
// as the promise in getData1AndData2 resolves ... valueS
// why don't you use the values from getData1AndData2 ??
return svc.getSources(24);
});
return promise;
};
svc.getSources=function(id){
var defer = $q.defer();
// it looks like you're having to use $q.defer
// as your Service.Query takes a callback rather than returning a promise
// if you control this then a promise would be much better
Services.query({Id:id}, function(data){
defer.resolve(data);
}); // the bracket ')' here was missing
return defer.promise; // this line was missing
};
I am having a service which is calling two other async services and returns some combined data, but how can this be done in AngularJS.
I have a controller in which I would like to call these two methods similar to this:
function ServiceC(serviceA,serviceB) {
var dataA = serviceA.GetAsyncStuff(); //returns promise
var dataB = serviceB.GetAsyncStuff(); //returns promise
return 'Greetings ' + dataA + dataB;
}
Is there a smarter way than to actually nest the Then method calls like this:
function ServiceC(serviceA,serviceB) {
var dataA = serviceA.GetAsyncStuff().then(function(respA){
var dataB = serviceB.GetAsyncStuff().then(function(respB){
return 'Greetings ' + respA.data + respB.data;
});
});
}
This example is of course a bit simplified.
Here is a demo plunker: http://plnkr.co/edit/yCrbwnarVDqwC4GBxhGg?p=preview
You need to use $q.all:
app.factory('myService', function($q, serviceA, serviceB, serviceC){
// $http returns a response object so we need to extract the data
var fn = function(res){
return res.data;
};
var promises = [
serviceA.GetAsyncStuff().then(fn),
serviceB.GetAsyncStuff().then(fn)
];
return $q.all(promises).then(function(data){
// you can manipulate consolidated data here
// data.push(serviceC);
return data;
});
});
From $q docs:
$q#all(promises);
Combines multiple promises into a single promise that is resolved when all of the input promises are resolved.
Returns
Returns a single promise that will be resolved with an array/hash of values, each value corresponding to the promise at the same index/key in the promises array/hash. If any of the promises is resolved with a rejection, this resulting promise will be rejected with the same rejection value.
function ServiceC(serviceA,serviceB) {
var _totalData = {};
var dataA = serviceA.GetAsyncStuff().then(function(respA){
_totalData = respA;
});
var dataB = serviceB.GetAsyncStuff().then(function(respB){
_totalData += respB;
});
return{ totalData: _totalData}
}
I say first one is better as it is having two different calls and you can add your data and return it at the end and and in your second example you using service under service that would creating a overhead for saving the value of one service till 2nd service get called and call its data, you can see the network pressure second one is taking much time to get the resources and to return the resources.
We can call both services and return promise.
myApp.provider("myService", function($http,$q){
var serviceOneData = {},
serviceTwoData = {};
//call service 1
return {
serviceOneData : serviceOneData ,
serviceTwoData : serviceTwoData ,
getService1: function(){
var deferred = $q.defer();
$http({method: 'GET',url:'Service1',
headers:{
'Access-Control-Allow-Origin': '*'}}).
success(function(data,status,header,config){
serviceOneData = data;
deferred.resolve();
})
.error(function (data,status,header,config) {
deferred.reject();
});
//call service 2
return deferred.promise;
},
getService2: function(){
var Seconddeferred = $q.defer();
$http({method: 'GET',url:'Service2',
headers:{
'Access-Control-Allow-Origin': '*'}}).
success(function(data,status,header,config){
serviceTwoData = data;
Seconddeferred.resolve();
})
.error(function (data,status,header,config) {
Seconddeferred.reject();
});
//call service 2
return Seconddeferred.promise;
},
getService : getService1().then(getService2()).
then(
return serviceOneData + serviceTwoData
);
}
} );
You can chaining promise:
angular.module('app',[])
.factory('Service1',function($q,$timeout){
return {
f: function(){
var deferred = $q.defer();
$timeout(function() {
deferred.resolve('Service 1');
},1000);
return deferred.promise;
}
}
})
.factory('Service2',function($q,$timeout,Service1){
return {
f: function(){
var deferred = $q.defer();
Service1.f().then(function(data){
$timeout(function() {
deferred.resolve('Service 2 '+ data);
},1000);
});
return deferred.promise;
}
}
})
.controller('MainCtrl', function($scope,Service2){
var promise2 = Service2.f();
promise2.then(function(data2){
console.log(data2);
});
});
or just use
$q.all([Service1,Service2]).then
http://denisonluz.com/blog/index.php/2013/10/06/angularjs-returning-multiple-promises-at-once-with-q-all/
Your best bet is to use $q.all, which makes sure that all of the requested promises have been resolved before attempting to execute a function
function ServiceC(serviceA, serviceB, $q) {
var dataA = serviceA.GetAsyncStuff(); //returns promise
var dataB = serviceB.GetAsyncStuff(); //returns promise
$q.all([dataA, dataB]).then(doSomething);
}
EDIT: If you need to return the data, you're going to have to return a promise. You can either just return $q.all or resolve a new promise once $q.all has been resolved, e.g.
function ServiceC(serviceA, serviceB, $q) {
var dataA = serviceA.GetAsyncStuff(); //returns promise
var dataB = serviceB.GetAsyncStuff(); //returns promise
var deferred = $q.defer();
$q.all([dataA, dataB]).then(function (promises) {
deferred.resolve('Greetings ' + promises[0] + promises[1]);
});
return deferred.promise;
}