angularJS $http request 'how to sync' - angularjs

I think you find this question thousands of times...but I can't really understand the way to solve.
I have a $http request inside a Service...
app.service('getData', function ($http) {
this.getDataList = function () {
$http.get('../content/catalog/data.json')
.success(function(response) {
return response;
})
};
I call it from the app.run
app.run(function (getData) {
list=getData.getDataList()
})
If I log the list variable is undefined
What is the way to sync them?
Thank you for the help!!!!

You're treating the call as if it were synchronous, where as its an Async call, so when you say return response; that line executes when the call hasn't finished yet and naturally you get undefined, use a callback instead or return a promise:
this.getDataList = function (callback) {
$http.get('../content/catalog/data.json')
.success(function(response) {
callback(response);
})
};
Usage:
getData.getDataList(function(data){
var list=data;
console.log(data);
});
EDIT:
Regarding promises, the idea is very simple, whenever you have an async operation that you expect will not be completed immediately, i.e. ajax calls, you can use a promise returned by the method making that async operation in order to find out when the task has finished.
For example $http returns a promise by default, so you can change your code to make use of that promise by simply returning the $http call itself:
this.getDataList = function () {
return $http.get('../content/catalog/data.json');
}
and then use it like this:
getDataList().then(function(successData){
var list=successData;
},function(errorResponse){
alert("something terrible has happened!");
})
The promise returned by $http takes 2 callbacks, the first for a successful call and the second is for errors.
Nowadays I mostly just pass callbacks in to the function making the async call, saves me from having to write then().

list is undefined because you forgot to return $http.get in this.getDataList()
By the way, $http will return a promise (an object with then and finally methods), which you can directly use in ng-bind for instance.

Use promises instead.
JavaScript uses promises for async(deferred) operations. These promises are based on callbacks(I promise to run your callback when I'm done).
getDataList does a http.get behind the scenes and doesn't block the code. It simply returns a promise object.
You can add callbacks to the promise object that will happen when the async operation finishes.
app.service('getData', function ($http) {
this.getDataList = function () {
return $http.get('../content/catalog/data.json');
};
});
app.run(function (getData) {
getData.getDataList().then(function(res){
list = res
});
});

Related

AngularJS: Chaining $timeouts and Promises

I am struggling with chaining promises using $timeouts. I would like to have a "$timeout(myFunction,1000).then()" function that fires only when ALL chained timeouts returned by myFunction are resolved.
This code snippet contains different stuff I tried and I would like to achieve:
$timeout(myFunction,1000).then(function(myPromise) {
console.log("I would like this message to appear when ALL chained promises are resolved, without knowing in advance how many chained promises there are. In this case this would be after 1500 ms, not 1000ms")
myPromise.then(function()) {
console.log("with this code patern I get a message after 1500ms, which is what I want, but this does not work anymore if myOtherFunction would return a third chained $timeout")
}
})
myFunction = function() {
console.log("hi, i am launching another timeout")
return $timeout(myOtherFunction, 500)
}
myOtherFunction = function () {
console.log("1500 ms have passed")
}
How should I fix my code? Thanks!
Return promises to the success handler:
$timeout(null,1000).then(function() {
console.log("It is 1000ms");
var delay = 500
return myPromise(delay);
// ^^^^^^ return promise for chaining
}).then(function() {
console.log("this happens after myPromise resolves");
});
function myPromise(delay) {
promise = $timeout(null, delay);
return promise;
});
Because calling the .then method of a promise returns a new derived promise, it is easily possible to create a chain of promises. It is possible to create chains of any length and since a promise can be resolved with another promise (which will defer its resolution further), it is possible to pause/defer resolution of the promises at any point in the chain. This makes it possible to implement powerful APIs.
-- AngularJS $q Service API Reference -- Chaining promises;
Inspired by the answer of georgeawg I created my custom timeout function that returns the promise returned by fct, instead of the promise returned by $timeout. I did this to keep the $timeout syntax.
vm.customTimeout = function (fct, timeout){
return $timeout(fct, timeout).then(function(myReturnedPromise){
return myReturnedPromise
});
}
This function is sufficient to solve my problem above. I can chain as much customTimeouts I want.
Example :
vm.customTimeout(myFunction,1000).then(function() {
var activity1 = anyFunctionReturningAPromise(100);
var activity2 = anyFunctionReturningAPromise(1000);
return $q.all([activity1, activity2])
console.log("Without knowing the content of myFunction, I am 100% sure that
every single chained promise retuned by myFunction is resolved before
executing this code, which is quite nice!")
}).then(function(){
console.log("executes when customTimeout, activity1 & activity2 are all resolved.")
})
anyFunctionReturningAPromise = function(delay) {
return vm.customTimeout(myFunction, delay)
}
Feel free to comment what you think of it.
I hope this will be useful for someone else :)

When do $q promises resolve

I have a following piece of code:
function getData(foos, bars) {
var generated = {};
return $q(function(resolve, reject) {
var promises = _.map(foos, function(foo) {
return _.map(bars, function(bar) {
return someServiceCall(foo, bar).then(function(data) {
_.set(generated[foo.id], player.id.toString() + '.data', data);
});
});
});
// Join all promises in to a final resolve
$q.all(promises).then(function() {
resolve(generated);
}, reject);
});
}
What I want to achieve is to have all the someServiceCall-s and it's success handlers finished by the time resolve(generated) is called. When I debug this part of the code in the developer toolbar, resolve(generated) is called before the success handler of someServiceCall is called for every promise.
Note: This not always breaks the functionality, as the objects are passed as a reference, so the data is set even if resolve was already called, but I think the functionality would be clearer if all those calls were finished by the time resolve is called.
I just realized my dumb mistake. The two nested maps resulted the following structure:
var promises = [[Promise, Promise][Promise, Promise, Promise]];
While $q.all expects an array of promises:
var promises = [Promise, Promise, Promise, Promise, Promise];
I could easily solve this by replacing the first map call by flatMap:
return _.flatMap(bars, function(bar) {
It's still strange for me that $q.all silently just resolved the promise without an error or warning that the data format is not appropriate.
I hope I can help someone who runs into this problem in the future.

How to implement EcmaScript2015 promises in Angular?

Is there a nice solution to get rid of Angular $q promise service and start using EcmaScript6 native promises?
I would say rather than using $q custom promise, you could utilize the promise return by the $http/$resource call itself. Because that has been taken care $http/$resource internal implementation, as they will run digest once their promise rejected/resolved.
The only benefit I can see is, by having ES2015 promise is, you could use Arrow function's (=>), which is shorter and smarter syntax.
Code
var promise = $http.get('someUrl');
promise.then((data) = > { //success callback
console.log(data)
}, (error) => { //error callback
console.log(error)
})

Order of execution of $http callbacks

It seems that factory methods execution priority is the highest, so that callbacks has no data to deal with. What is the best way to make this work?
I got this kind of factory
app.factory('jsonService', function($http) {
return {
getDistricts: function(callback) {
$http.get('data/districts.json').success(callback);
},
getLocations: function(path,callback) {
$http.get('data/'+path+'.json').success(callback);
}
};
});
And controller
var app = angular.module('sandbox', []);
app.controller('sandboxCtrl',function ($scope,jsonService) {
//This one works
$scope.init1= function(){
jsonService.getDistricts(function(data){
$scope.districts = data;
$scope.currentDistrict = $scope.districts[0].name;
jsonService.getLocations($scope.currentDistrict,function(data){
$scope.locations1 = data;
})
});
};
$scope.init1();
//This one does not
$scope.init2= function(){
jsonService.getDistricts(function(data){
$scope.districts = data;
$scope.currentDistrict = $scope.districts[0].name;
})
jsonService.getLocations($scope.currentDistrict,function(data){
$scope.locations1 = data;
});
};
$scope.init2();
});
Here is working plunker
Angular has an implementation of promises named $q (documentation) that you should read up upon.
There is a race condition due to the async nature of http calls. Please review the updated code linked to below that shows an example of your code running (successfully) using promises to handle your two calls in succession.
So upon success of your first call it will call your second service method all without using callbacks thanks to the power of promises.
jsonService.getDistricts()
.success(function(data) {
$scope.districts = data;
$scope.currentDistrict = $scope.districts[0].name;
jsonService.getLocations($scope.currentDistrict)
.success(function(locationData) {
$scope.locations = locationData;
})
});
updated PLKR
Promise clarification:
The raw implementation of basic promises uses then to handle responses and promises returned from $http add additional methods (success, error) that will unpack your data from the response object that you would need to handle if your just use then.
init1() is the correct way of doing this. init2() does work because jsonService.getLocations() is getting invoked before jsonService.getDistritcs() completes. The angular $http service is asynchronous. Since jsonService.getLocations() depends on data from jsonServicd.getDistricts() you must wait until .getDistricts() completes before calling .getLocations(). One way to do that is to call .getLocations() within the .getDitricts() callback, just as you did in init1().

angular using $http in factory

I seem to be having an issue sending json from my factory out to controllers
Here is my factory
.factory("UserService", function($http) {
var LevelsHere;
$http.get("/assets/images/generated.json").success(function(data){
LevelsHere = data;
return LevelsHere;
});
return {
all: function() {
return LevelsHere;
},
first: function() {
return LevelsHere[0];
}
};
})
I am simply trying to send the json object out (or bits of it) with this factory. I can console.log inside the http get and it seems to be grabbing the json just fine. I seem to have hit a wall, any help would be much appreciated. I would just like the all ad first functions to be working. thanks!
I first had success by hard coding the levelsHere above it with the json string like var levelsHere = [{"stuff in here"}], but when i moved it over to an $http it doesn't work.
Since you don't have any $watch to look over the value returned from asynchronous $http.get request, the updated value is not available to the consumer. As $http.get request returns a promise, you can leverage the promise and update the value on success of the promise in then() as below:
var app = angular.module('app', [])
.factory("UserService", function($http) {
var LevelsHere = $http.get("https://api.github.com/users/mralexgray/repos")
.success(function(data){
return data;
});
return {
all: function() {
return LevelsHere;
}
};
})
.controller('controller', function(UserService, $scope){
UserService.all().then(function(data){
$scope.value = data;
});
})
DEMO
What is not working exactly? My guess is that you got undefined immediately after this factory method, since $http uses deferred object
You are returning LevelsHere before the async call is finished. The order of your operation goes:
call http.get
return all and first which return LevelsHere (even though the http request has not finished)
http get returns json
success call back fires returning LevelsHere to nobody.
A better way is to just return the promise:
return $http.get("/assets/images/generated.json")
then in your controller you can get the value from the promise by calling the success function. If you try to resolve the promise in the factory and return the value, your controller will try to use the value before it's returned from the server.
var promise = UserService()
promise.success(function(data){
// do something with data
}

Resources