Understanding angular's promises - javascript

I'm learning Angular but having troubles to understand a point. I want to get data from my server. I used this code (from angular's doc) :
this.myFunction = new function()
{
var uri = this.getServerUri() + "Connexion/"+login+"/"+CryptoJS.MD5(pass);
var promises = $http.get(uri)
.success(function(data, status, headers, config) {
// this callback will be called asynchronously
// when the response is available
//alert("success:"+JSON.stringify(data));
return data;
}).
error(function(data, status, headers, config) {
// called asynchronously if an error occurs
// or server returns response with an error status.
return "";
});
return promises;
};
But I don't understand the behavior of the success function. When I call this function, it works fine, I get the server's answer but I get the full promises object (I have to write "promises.data" to get my data). Could you explain me why ? Because in the success function I tried to return only the data.
EDIT : I forgot to add my calling function :
var serverAccepted = this.MyFunction().then(function(promise) {
var objet = promise.data;
if(!_.isEmpty(objet))
{
userService.setUser(objet, true);
return "";
}
else return "Error while trying to login on the server";
});
return serverAccepted;
Thank you

A promise is a way of dealing with asynchronous requests without blocking. Instead of waiting for a slow operation (like waiting for the server to respond), before executing any more code, a promise is returned and code execution continues.
As such, when you call myFunction in your code above, it will return a promise object.
The promise then provides some neat methods for registering a callback to run code when the server does respond. These methods are .then() and catch() for the success and error case respectively.
Passing a function as an argument will call that function when the server has responded, and you can do with the data what you like. Take this example:
console.log("About to send request");
myFunction()
.then( function (myData){
console.log("Response received", myData);
})
.catch( function (myError){
console.error("Oh dear", myError);
});
console.log("Request sent!");
This will log the following to the console:
About to send request
Request sent!
Response received, {}
EDIT
In response to your comment:
why does the response received contains the full promise object and not only the data?
Because I return the data in the success function.
The response you receive does not contain a promise at all, but your function returns a promise. In your example serverAccepted is assigned the return value of myFunction() which is a promise and will not change.
The line:
.success(function(data, status, headers, config) {
return data;
})
Doesn't really do anything at all - returning a value from a .then() or .success() function is only useful if you are going to chain several calls to .then() together - it provides a way of manipulating values without an ever increasing pyramid of nested callbacks. Returning a value from within a promise callback (function passed to .then() or .success()) will not change the promise itself (serverAccepted in your case).
For example:
assume http GET some/url on your server returns:
{
foo: 'bar',
dorks: 12
}
When we use $http.get This data is inside the response object as a field called data. Most of the time we don't care much about the rest of the stuff, if the call was successful, we just want the data, so we can return it from the first .then() call (normally inside your service) so that any subsequent .then() calls (e.g. inside a controller) can access the data object directly.
myPromise = $http.get('some/url')
.then( function (response) {
console.log(response); //--> { status: 200, data: { foo:....}}
return response.data;
})
.then( function (data) {
console.log(data); //--> {foo: 'bar', dorks: 12}
return dorks;
})
.then( function (data) {
console.log(data); //--> 12
})
console.log(myPromise); //--> promise object with .then() .catch() etc
// ^ THIS WILL NOT CHANGE.

When I call this function, it works fine, I get the server's answer but I get the full promises object
Sounds like you are doing something like this:
var data = service.myFunction();
Above would be fine in synchronous world, however in case of asynchronous operations this is not correct usage of the promises. Instead you should use returned promise then/success methods:
service.myFunction().then(function(data) {
console.log(data);
});

$http request ,returns a promise with two $http specific methods: success and error. success function waits until request is to be finish with success (that means after $http promise resolves), and error function will execute only after the $http promise is rejected.

Related

"TypeError: response.json is not a function" when trying to parse json

I am getting an error I do not understand. I'm fetching an API url in json format, followed by a json to JS object parsing, using json()
const response = fetch('https://power.larc.nasa.gov/api/temporal/monthly/point?parameters=ALLSKY_SFC_SW_DNI&community=RE&longitude=48.0000&latitude=27.0000&format=JSON&start=2001&end=2020');
const data = response.json();
Can someone please explain this error..
fetch is an asynchronous function; it doesn't return the response right away (you get a Promise instead).
If you want to fetch the data, you need to use the await (if the fetch is called inside another async function), or provide a callback via then method in the Promise from the fetch call:
fetch('https://power.larc.nasa.gov/api/temporal/monthly/point?parameters=ALLSKY_SFC_SW_DNI&community=RE&longitude=48.0000&latitude=27.0000&format=JSON&start=2001&end=2020')
.then(response => {
const data = response.json();
});
Working with data from response
If you would do something like this, it won't work:
let data;
fetch('https://power.larc.nasa.gov/api/temporal/monthly/point?parameters=ALLSKY_SFC_SW_DNI&community=RE&longitude=48.0000&latitude=27.0000&format=JSON&start=2001&end=2020')
.then(response => {
data = response.json();
});
console.log(data);
The reason is because the callback inside the then method is executed after the response has been returned (this might take seconds for example), but the console.log(data) is executed immediately after the fetch is called. This means that the data isn't assigned yet and will be probably undefined. For this reason, if you want to put data somewhere, you need to put your code inside the callback.
function processData(responseData) {
// Here, you can put your code to process the data from response
console.log(responseData);
}
fetch('https://power.larc.nasa.gov/api/temporal/monthly/point?parameters=ALLSKY_SFC_SW_DNI&community=RE&longitude=48.0000&latitude=27.0000&format=JSON&start=2001&end=2020')
.then(response => {
const data = response.json();
processData(data);
});
This way the data will be processed after it is fetched.
Fetch returns a Promise since you are sending a request to an API. This type of functions is asynchronous since we don't know when we're getting back the response from the API.
You can either use async/await or .then() and .catch() to execute your code when you get back the response.
Try this:
let returnedData;
fetch('https://power.larc.nasa.gov/api/temporal/monthly/point?parameters=ALLSKY_SFC_SW_DNI&community=RE&longitude=48.0000&latitude=27.0000&format=JSON&start=2001&end=2020')
.then((data) => {
console.log(data)
returnedData = data
})
.catch((error) => {
console.log(error)
})

promises in angular - POST request

I'm new to programming and was wondering why do you not need a promise on a post request, but you do on a get request?
var getAll = function() {
return $http({
method: 'GET',
url: '/api/links'
}).then(function(resp) {
return resp.data;
})
}
var addLink = function(link) {
return $http({
method: 'POST',
url: '/api/links',
data: link
})
//you don't need a promise on a POST!
}
Well, $http always returns a promise on both GET and POST. In your case, you need to do something with the data that comes back from your GET, so you're correctly using the promise's then() to run some code after the HTTP request finishes.
In your POST, it doesn't look like you care about the response, so you're simply choosing not to do anything. You could just as easily tack a then() onto your POST as well for any number of reasons.
Also, promises chain, so then() also returns a promise. Whatever code calls getAll() and addThis() can do something with the promise too. However, something that does getAll().then(function(data) { ... }) will have the data from the HTTP response passed to it because of the return resp.data you have up there.
Code that does addThis().then(function(data) { ... }), in this case, will have the entire HTTP response passed to it since there's nothing processing it like in getAll().
Because when you GET you are going to return a response which generally requires the request to wait for some sort of response. POST you don't necessarily need to wait for a response. As a note, just because you don't "need" a promise on a POST doesn't mean you can't have one. You could add one if you so desire.

How can I retrieve HTTP status codes using Angular's $q promises?

I have within my code that makes an $http.post to a back end, and of course things can go wrong. If I use the below syntax within angular:
$http.post(url).success(function(data, status, headers, config))
.error(function(data, status, headers, config));
If there is an error, I can retrieve the error code using status and handle it within the function defined in error(). However, if instead I take this approach (which is the one I am in fact using):
var deferred = $q.defer();
$http.post(url).success(deferred.resolve).error(deferred.reject);
return deferred.promise;
With the second syntax, I have all of my ajax calls within a separate ServiceJS directory, and can handle the successes or errors on a case by case basis. For instance, if the second snippet was Service.MyFunction() then in the code where I need it I would:
Service.MyFunction().then(function() {},
function(data, status, headers, config) {});
However, if I use this code block, status, headers, config are all undefined. My question is how can I retain this syntax but still retrieve the error code?
As an added reference, the back end is a C# Web API project, which would return errors using return BadRequest(); or the like.
Try something like this:
myFunction(){
var deferred = $q.defer();
// you can use .then() instead of .success or .error
$http.post(url).then(function(successResponse){
var data = successResponse.data;
var status = successResponse.status;
...
deferred.resolve(successResponse);
}, function(failureResponse){
var status = failureResponse.status;
var config = failureResponse.config;
...
deferred.reject(failureResponse);
});
return deferred.promise;
}
well, I'd say it's a good practice and more standard to implement a http interceptor and handle the HTTP errors from there intead of handling the error one by one on each http or resource object, and your code will be located in a single location.
basically, you can segment the actions to take depending on the error status you get for example:
angular.module('app').factory('myInterceptor', ['$q',
function($q){
return {
'responseError': function(rejection){
switch(rejection.status){
case 0:
//'No connection, is the internet down?'
break;
case 422:
// error
break;
case 484:
// error
break;
default:
// message rejection.status, rejection.statusText
break;
}
return $q.reject(rejection);
}
};
}
]);
$http is already returning a promise, so why not use that one?
function myFunction() {
return $http.post(url);
}
// ...
myFunction().success(function(data, status, headers, config) {
// ...
});
... or ...
myFunction().then(...);
The promises returned from $http have the methods success and error in addition to the other promise methods. Read more at Angular docs for $http.

Modifying the argument passed to jQuery done() callback following a successful AJAX call

Here's the scenario:
I need to fetch some JSON data via AJAX from the server (ASP.NET MVC application + ServiceStack services). If the user's session expires then instead of getting back JSON I get a redirect to the login page, which gets automatically executed by jQuery, so the result of the AJAX call ends up being the raw HTML of the login form. I need to handle these two cases differently:
if the result is JSON then use it to update the UI using the .done() promise
if the result is HTML then pop up a login dialog.
So I have to modify the argument passed to the done() callback. Here's what I've tried so far:
function getSomeData()
{
return $.ajax(
{
/*
...set my AJAX config options...
*/,
error: function(xhr, status, errorText)
{
// handle the error
},
success: function(data)
{
// if the AJAX call returned a login form instead of expected JSON
// result (because the user's session has expired) then show a
// login dialog and resolve the jQuery promise with 'null' instead
// of original 'data'
if (isLoginFormHTML(data))
{
showModalLoginDialog(data);
data = null; // neither this...
return null; // ... nor this seem to work!
}
}
});
}
function updateUI()
{
getSomeData().done(function(jsonData)
{
if (jsonData)
{
// do something
}
});
}
Unfortunately, regardless of whether I do data = null or return null in my success function, the argument passed to the done() callback contains the original data returned from the server, which may be the HTML of the login form if the session has expired.
So the question is, how do I tell jQuery to pass something else to done()?
P.S. Of course, I could perform the isLoginFormHTML() check inside the updateUI method but I'm trying to avoid that in order to keep the data-retrieval and UI-update logic separate.
You should return a promise on the values you need by using .then, one of the cool things about promises is that they chain.
function getSomeData()
{
return $.ajax({
/*
...set my AJAX config options...
*/,
error: function(xhr, status, errorText)
{
// handle the error
}).then(function(data){
if (isLoginFormHTML(data)) {
showModalLoginDialog(data);
throw new Error("Unauthorized");..
}
return data;
}
});
}
Which would allow you to do:
getSomeData().done(function(jsonData){
//yay data
}).fail(function(){
//no data
});

Angular issue with Q library

I am following the tutorial related to Angular.js that is located on pluralsight.com. So far I did not have serious issues. Tutorials are very good and easy to follow. However, there is something that I cant resolve on my own. I would like my deffereds to react differently either on success or on fail. In this case they are always firing like they succeeded.
Service:
var resource = $resource('/data/event/:id', {id: '#id'});
return {
getEvent: function (id) {
var deferred = $q.defer();
resource.get({id: id},
function (event) {
console.log("This is (EVENT): " + event);
deferred.resolve(event);
},
function (response) {
console.log("This is (RESPONSE): " + event);
deferred.reject(response);
});
return deferred.promise;
Controller:
$scope.event = eventData.getEvent(2)
.then (
function(event) {
$scope.event = event;
console.log(event);
},
function(response) {
console.log(response);
}
);
In other words, if I send the wrong id (to load JSON file that does not exists) I want it to let me know that.
The $resource service in angular wraps a call to the $http service applying some REST conventions to the HTTP request. These are all documented here in the $resource docs.
When handling the promise from $resource, if the HTTP status code is 200, then the success callback will be executed. Otherwise (if the HTTP status code is in the 400 or 500 range), the error callback will be executed. This is a typical REST convention.
So, since your service is always returning 200 Status codes, $resource assumes that this is a successful server call and executes the success callback.
In order to handle this, you would need to use $http directly or change your service so that it returns the correct HTTP status code.
Hope this helps.

Categories

Resources