Asynchronus calls in angularjs - javascript

I am new to Javascript and Angularjs. I wanted to know , how to call a function asynchronously without waiting for it to return from it.
Please let me know and if there is some example then it would be very helpful.
Regards,
nG

Use Angular's deferred:
function myAsyncFunction() {
var deferred = $q.defer();
//..
setTimeout(function() {
deferred.resolve({ message: "resolved!" });
// or deferred.reject({ message: "something went terribly wrong!!" });
}, 1000);
//..
return deferred.promise;
}
myAsyncFunction()
.then(function(data){
// success
console.log("success", data.message);
}, function(data) {
// fail
console.log("error", data.message);
}).finally(function() {
// always
});

You can use a deferred to return a promise, you can then resolve the promise once your function is complete. Try something like this:
http://jsfiddle.net/adcoxwga/
var myApp = angular.module('myApp',[])
.service('myService', function($q, $timeout){
this.someFunction = function(){
var deferred = $q.defer(); //making a new deferred
$timeout(function(){
deferred.resolve('something'); //resolving the deferred with a response
}, 3000);
return deferred.promise; //returning a promise
}
})
.controller('MyCtrl', function($scope, myService){
myService.someFunction().then(function(response){ //calling the asynchronous function
console.log(response); //getting response
}, function(error){
console.log(error); //getting error
});
});

You have a couple of different options ahead of you, but one thing to note is that you have to use a callback or promise for asynchronous activity. Since you are using angular, I'd suggest using promise's.
Using a promise
If you are writing an http call to an api, you can use either $http or $resource. If you start to research angular providers, you will see that $http returns a promise, while $resource returns a promise but you must call it specifically to access it. For instance:
someService
function someServiceFunction () {
return $http.get('some/url');
}
someController
$scope.results = [];
someService.someServiceFunction().then(function(data) {
$scope.results = data;
});
Something to note is that the first returned function is the success scenario and if you declare a second callback, then it is the failure scenario.
If you were to use callbacks in the same scenario:
Using a callback
someService
function someServiceFunction (callback) {
return $http.get('some/url')
.success(callback);
}
someController
$scope.results = [];
someService.someServiceFunction(function(data, status, headers, config) {
$scope.results = data;
});
Here's a link to the $http provider.
Here's a link to the $resource provider.
Cheers!

Related

Angular 1.6.4 $http post callback not called [duplicate]

I'm finding it hard to understand the "deferred antipattern". I think I understand it in principal but I haven't seen a super simple example of what a service, with a differed promise and one with antipattern, so I figured I'd try and make my own but seeing as how I'm not super in the know about it I'd get some clarification first.
I have the below in a factory (SomeFactory):
//url = 'data.json';
return {
getData: function(){
var deferred = $q.defer();
$http.get(destinationFactory.url)
.then(function (response) {
if (typeof response.data === 'object') {
deferred.resolve(response.data);
} else {
return deferred.reject(response.data);
}
})
.catch(function (error) {
deferred.reject(error);
});
return deferred.promise;
}
The reason I am checking its an object is just to add a simple layer of validation onto the $http.get()
And below, in my directive:
this.var = SomeFactory.getData()
.then(function(response) {
//some variable = response;
})
.catch(function(response) {
//Do error handling here
});
Now to my uderstanding, this is an antipattern. Because the original deferred promise catches the error and simply swallows it. It doesn't return the error so when this "getData" method is called I have do another catch to grab the error.
If this is NOT an antipattern, then can someone explain why both require a "callback" of sorts? When I first started writing this factory/directive I anticipated having to do a deffered promise somewhere, but I didn't anticipate having to .catch() on both sides (aka I was sort of thinking I could get the factory to return the response or the error if I did a SomeFactory.getData()
Is this a “Deferred Antipattern”?
Yes, it is. 'Deferred anti-pattern' happens when a new redundant deferred object is created to be resolved from inside a promise chain. In your case you are using $q to return a promise for something that implicitly returns a promise. You already have a Promise object($http service itself returns a promise), so you just need to return it!
Here's the super simple example of what a service, with a deferred promise and one with antipattern look like,
This is anti-pattern
app.factory("SomeFactory",['$http','$q']){
return {
getData: function(){
var deferred = $q.defer();
$http.get(destinationFactory.url)
.then(function (response) {
deferred.resolve(response.data);
})
.catch(function (error) {
deferred.reject(error);
});
return deferred.promise;
}
}
}])
This is what you should do
app.factory("SomeFactory",['$http']){
return {
getData: function(){
//$http itself returns a promise
return $http.get(destinationFactory.url);
}
}
while both of them are consumed in the same way.
this.var = SomeFactory.getData()
.then(function(response) {
//some variable = response;
},function(response) {
//Do error handling here
});
There's nothing wrong with either examples(atleast syntactically)..but first one is redundant..and not needed!
Hope it helps :)
I would say that it is the classic deferred anti-pattern because you are creating needless deferred objects. However, you are adding some value to the chain (with your validation). Typically, IMO, the anti-pattern is particularly bad when deferred objects are created for very little or no benefit.
So, the code could be much simpler.
$q promises have a little documented feature of automatically wrapping anything returned inside a promise in a promise (using $q.when). In most cases this means that you shouldn't have to manually create a deferred:
var deferred = $q.defer();
However, that is how the documentation demonstrates how to use promises with $q.
So, you can change your code to this:
return {
getData: function(){
return $http.get(destinationFactory.url)
.then(function (response) {
if (typeof response.data === 'object') {
return response.data;
} else {
throw new Error('Error message here');
}
});
// no need to catch and just re-throw
});
}
Using the $q constructor is a deferred anti-pattern
ANTI-PATTERN
vm.download = function() {
var url = "https://www.w3.org/WAI/ER/tests/xhtml/testfiles/resources/pdf/dummy.pdf";
return $q(function(resolve, reject) {
var req = {
method: 'POST',
url: url,
responseType: 'arraybuffer'
};
$http(req).then(function(response) {
resolve(response.data);
}, function(error) {
reject(error);
});
});
}
CORRECT
vm.download = function() {
var url = "https://www.w3.org/WAI/ER/tests/xhtml/testfiles/resources/pdf/dummy.pdf";
var req = {
method: 'POST',
url: url,
responseType: 'arraybuffer'
};
return $http(req).then(function(response) {
return response.data;
});
}
The $http service already returns a promise. Using the $q constructor is unnecessary and error prone.

AngularJS factory returning State object instead of JSON data from promise

I have made two factory and calling first from second one, consuming second one factory in controller but instead of getting data as JSON, I'm getting data as $$State.
I'm new to angularJS tried many ways but not able to solve, please help.
app.factory('userDetails', ['APIService', '$q', function (APIService, $q) {
getDetails: function () {
var deferred = $q.defer();
var getFunc = function () {
APIService.doGetCall('Masters/employee/username/tarun')
.then(
function (data)
{
deferred.resolve(data);
},
function (data, status, headers, config)
{
deferred.reject('There was an error creating object'); })
return deferred.promise;
}
var a = getFunc();
return a;
}
}
vm.user = userDetails.getDetails();
console.log("user",vm.user);
response is as per below snippet
Response
You should be using .then function to get response there. Rather I'd say you don't need to create extra promise, its an anti-pattern. Where you could utilize the promise return by doGetCall method directly.
app.factory('userDetails', ['APIService', '$q', function(APIService, $q) {
getDetails: function() {
var getFunc = function() {
return APIService.doGetCall('Masters/employee/username/tarun');
}
var a = getFunc();
return a;
}
}]);
vm.user = userDetails.getDetails().then(function(response){
console.log(response.data);
vm.user = response.data;
console.log("user", vm.user);
}, function(error){
console.log(error)
}).catch(exceptionHandler);
userDetails.getDetails() returns a promise, Hence you need to use .then(onsuccess, onRejected)
userDetails.getDetails().then(function(data){
vm.user = data;
console.log("user",vm.user);
});
You've got the
return deferred.promise;
line inside the APIService.doGetCall function. It should be outside of it.

Angular httpPromise

I have a request function :
function search(request) {
return $http.post('/path/to/resource', request);
}
I can call it like this :
search({/*...*/})
.success(function() {})
.error(function() {})
As I often need to find some objects by their ID, I need a shortcut function. I cannot find how to create this function so that I can also chain it with success() and error() functions.
I searched how to create a promise in angular and found the documentation about $q and this is what I tried :
function searchById(id) {
var deferred = $q.defer();
search({id: id}).
then(function (response) {
deferred.resolve(response.data.results[0]);
}, function (error) {
deferred.reject(error);
});
return deferred.promise;
}
I can only call it like this :
searchById().then(successCallback, errorCallback);
I would like to be able to call it like this :
searchById()
.success(successCallback)
.error(errorCallback);
The documentation about $q indicates that it returns a promise whereas the documentation about $http indicates that it returns an httpPromise but I cannot figure how to create an httpPromise.
Any idea?
In angular promises the error callback should be catch not error, try this
searchById()
.then(successCallback)
.catch(errorCallback);
sjokkogutten is correct that you don't need to use $q in this case you can simplify this
var deferred = $q.defer();
search({id: id}).
then(function (response) {
deferred.resolve(response.data.results[0]);
}, function (error) {
deferred.reject(error);
});
return deferred.promise;
to this
return search({id: id}).
then(function (response) {
return response.data.results[0];
}
$http is already returning a promise, so there is no need to use $q.defer(). Also, success() and error() has been depreciated (since 1.4.4), you should use then() instead.
Call your function like this:
search(request).then(function(data){
// successcallback
}, function(error){
// errorcallback
})
Creating a factory with $http functions will allow you to use .success and .error. But you really should be using .then.
app.factory("dataService", function($http) {
return {
search: function() {
return $http.get('path/to/api');
},
searchById: function(payload) {
return $http.post('path/to/api', payload);
},
searchMoreThings: function(payload) {
if(payload === "foo") payload = "bar";
return $http.post('path/to/api', payload);
}
}
});
You can do:
dataService.searchById(searchTerm).success().error();
Here is an actual example :
app.controller('controller',function($scope,$rootScope,$http,){
$scope.login = function(request){
var promise = $http.post(/path/to/resource, request, {
headers: {
'Content-Type': 'application/json'
}
}).then(function success(res){
//it worked, you have data in res
},function error(res){
// it failed
});
};
});

AngularJS: Why is .then() not waiting for the promise object to arrive

I have created the following angular service for the purpose of making the API calls.
(function (module) {
module.service('APIService',function ($http, $q) {
console.log("Reached APIService")
var deferred = $q.defer();
this.getData = function (requestURL) {
console.log("Reached APIService #GET", requestURL);
$http.get(requestURL).success(
function (data, status) {
deferred.resolve(data);
console.log("In service",status,data);
}).error(
function (data, status) {
deferred.reject(data);
console.log(status);
});
return deferred.promise;
};
this.postData = function (requestURL, requestObj) {
console.log("Reached APIService #POST", requestURL, requestObj);
$http.post(requestURL, requestObj).success(
function (data, status) {
deferred.resolve(data);
console.log(status);
}).error(
function (data, status) {
deferred.reject(data);
console.log(status);
});
return deferred.promise;
};
});
}(angular.module("MainApp")));
I have injected it in my two controllers. However, I am facing following issues:
When I call it first time in first controller, it works fine and returns the desired result. However, when I call it in second controller as follows:
APIService.getData(Config + headerURL).then(function (response) {
console.log(Config + headerURL);
console.log("header response from API", response);
},function(error) {
console.log("API call for header data failed");
});
Since my service returns a promise object, I don't expect the code inside .then() to work before the service data arrives.
However, it runs before service data arrives (no clue how). The most strange thing is that the response that I get inside .then() is not actually the response from this URL (Config+headerURL), but it is the response that was obtained from a different URL previously called in first Controller using the same APIService service.
Just to inform: the actual response from current URL do arrive at a later stage.
I am aware of asynchronous callings and I think it has something to do in this case, but I guess .then() handles it in Angular. So I am confused what is the issue here. Can anyone shed some light please?
Since the service is a singleton, you only have a single instance of the deferred object.
Once resolved, it will keep being resolved, so the next time you call, getData, it will return immediately.
You could move:
var deferred = $q.defer();
inside both your getData and postData function.
Or you could just return the promise that $http creates.
Just try this, You need to return to the http result.
this.getData = function (requestURL) {
console.log("Reached APIService #GET", requestURL);
return $http.get(requestURL) };
Looks like you have to disable cache explicitly within $http call.
$http.get({
url: requestURL,
cache: false
})
You are using an anti-pattern by creating your own promises when $http already returns a promise.
Get rid of $q altogether in this service and simply return $http
this.getData = function (requestURL) {
console.log("Reached APIService #GET", requestURL);
return $http.get(requestURL).then(function (response) {
return response.data;
console.log("In service", status, data);
}, function () {
// errror handling here
});
};

Angularjs get object values outside of function

I need to get localeData value print out side of below function.
I know how to print in side of function like what i did here.
I have tried this but didn't work.
$http.post(SERVER_URL + 'getLocaleData').success(function(localeData) {
console.log(localeData);
}).error(function(err) {
alert('warning', err.message);
});
//need to get that value here.
console.log(localeData);
EDIT
Actually i need to do is this
app.factory('customLoader', function($http, $q, SERVER_URL) {
return function(options) {
var deferred = $q.defer();
// do something with $http, $q and key to load localization files
$http.post(SERVER_URL + 'getLocaleData').success(function(localeData) {
//do something here to localData
}).error(function(err) {
alert('warning', err.message);
});
deferred.resolve(localeData);
return deferred.promise;
};
});
This is what i need. finally i need to send localeData.
You don't need to create & resolve a defer yourself. There is an easier way. (Also in the example you give, you resolve promise before the underlying ajax completed)
Angular.js $http service follows $q interface too! So you can write:
app.factory('customLoader', function($http, $q, SERVER_URL) {
return function(options) {
var promise = $http.post(SERVER_URL + 'getLocaleData')
.then(function(localeDataBag) {
var localeData = localeDataBag.data; // .data of 'then' equals to what .success returns.
modifiedLocaleData = localeData++; // do something here to localData
return modifiedLocaleData; // This is the new result
})
.catch(function(err) {
console.log(err);
return $q.reject(err); // This is the new result if something failed. Since we are chaining promises, we should keep failure state.
});
return promise;
};
});
You don't need $q to accomplish your objective:
app.factory('customLoader', ["$http", "SERVER_URL", function($http, SERVER_URL) {
return function(options) {
return $http.post(SERVER_URL + 'getLocaleData');
};
}]);
Because $http and all it's convenience methods actually return a promise. In this case, you can use this service as follows:
customLoader(options).success(function(localeData){
//do something here to localData
}).error(function(err){
alert('warning', err.message);
});
If you really must use $q, then this should do:
app.factory('customLoader', ["$http", "$q", "SERVER_URL", function($http, $q, SERVER_URL) {
return function(options) {
return $q(function(resolve, reject){
$http.post(SERVER_URL + 'getLocaleData').success(function(localeData) {
//do something here to localData
resolve(localeData); // ultimately you ought to resolve inside this function
}).error(function(err) {
alert('warning', err.message);
reject(localeData); // ultimately you ought to reject inside this function
});
});
};
}]);
JS is asynchronous, which basically means that your last console.log will be executed before your server returned any data. So you have to do it inside both your promises:
$http.post(SERVER_URL + 'getLocaleData').success(function(localeData) {
console.log(localeData);
}).error(function(err) {
console.log(err);
alert('warning', err.message); //Just in case you didn't know, this line supposes that your server returns a JSON object with a "message"-property
});
That way, your server-response will be console.log'ed both on success and failure.
Reference
Edit: If you really wanna do it the other way:
var response; //First, declare a variable in the parent scope.
$http.post(SERVER_URL + 'getLocaleData').success(function(localeData) {
response = localeData; //Assign the response data to the variable.
console.log(localeData);
}).error(function(err) {
alert('warning', err.message); //Just in case you didn't know, this line supposes that your server returns a JSON object with a "message"-property
});
console.log(response);
The example won't evaluate as you expect, though. The last console.log will just log undefined.
By following way you can access your server response outside factory in the controller:
app.factory('customLoader', function ($http, $q) {
return {
response: {},
getLocalData: function (param) {
var deferred = $q.defer();
$http.post(SERVER_URL + 'getLocaleData', param).
success(function (data, status, headers, config) {
deferred.resolve(data);
}).
error(function (data, status, headers, config) {
});
return deferred.promise;
}
}
});
app.controller('yourCtrl', function($scope,customLoader) {
$scope.GetLocaleData= function() {
customLoader.getLocalData({
Role: role,
Email_Id: $scope.Email,
Pwd: $scope.Password
}).then(function(localeData) {
$scope.Data = localeData;
});
}
});
localeData is limited to the scope of .success function. You cant get it outside unless you assign it some other variable.
var data = ""
$http.post(SERVER_URL + 'getLocaleData').success(function(localeData) {
data = localeData;
}).error(function(err) {
alert('warning', err.message);
});
//need to get that value here.
console.log(data);
But dont expect to get the desired value in data until you get the response back. $http.post is async
Take a look at promises to overcome such issues, because they are used all over the place, in angular world.
UPDATE:
As you have done in the edit, you may either use $q service like:
app.factory('customLoader', function($http, $q, SERVER_URL) {
return function(options) {
var deferred = $q.defer();
// do something with $http, $q and key to load localization files
$http.post(SERVER_URL + 'getLocaleData').success(function(localeData) {
//do something here to localData
}).error(function(err) {
alert('warning', err.message);
});
deferred.resolve(localeData);
return deferred.promise;
};
});
OR, you may simply return $http.post which itself returns a promise
app.factory('customLoader', function($http, $q, SERVER_URL) {
return function(options) {
return $http.post(SERVER_URL + 'getLocaleData');
};
});
In either way, you are returning a promise object and not just the actual response you get from server. The localeData can be accessed as follows:
customLoader().success(function(res){
console.log(res);
}).error(function(){
//show some error message
})

Categories

Resources