angularjs $q then() > catch() > then(), cancel execution of last then() - angularjs

I have the following code (simplified for brevity):
var makeRequest = function (method, url, query, data) {
var request = {
method: method,
url: url,
params: query || {},
paramSerializer: '$httpParamSerializerJQLike',
data: data || {},
timeout: 10000
};
if (method === 'POST' || method === 'PUT') {
request.headers = { 'Content-Type': 'application/json; charset=UTF-8' };
}
return $http(request)
.then(function (response) {
response = normalizeResponse(response.data); // ensures {result, data, msg}
if (!response.result) {
throw {data: response};
}
return response;
})
.catch(function (response) {
Notify('error', response.data.msg);
});
};
Calling it looks like this:
makeRequest('GET', '/user').then(function (response) {
console.log('user=', response.data);
});
This works fine for successful requests, but as soon as I make a failing request, I get a TypeError: Cannot read property 'data' of undefined, since catch() does not (and should not) return anything, but the second then() promise is still executed.
I want catch() to be the last thing that executes, and any further then() calls to not execute if the request itself fails. The catch block is universal across the system, so it doesn't make sense to copy-paste it everywhere, it's not DRY. Is it possible or do I have to do some kind of dirty hacks for this?
Note: I still want the promise chaining to be available if request succeeds.

A catch block "rescues" the promise and allows .then calls later on to proceed as normal. If you want to avoid this behavior, just re-throw the error in the catch block.
.catch(function (response) {
Notify('error', response.data.msg);
throw response.data.msg;
});

Related

How return the promise of $http in angularJs

I am new to AngularJS. How can I return the response of "response.data" as a typical function?
Because $http generates a promise, when the function finishes it doesn't return the server response.
In my controller I have:
this.message2 = function() {
$http({
url : 'dataset_of_model',
method : "POST",
data : {
'experiment' : 'rcp85',
'model' : 'HadGEM2-ES',
'frequency' : 'day'
}
}).then(function(response) {
console.log('qui');
console.log(response.data);
return response.data;
}, function(response) {
//fail case
console.log(response);
console.log(fallito);
return response;
});
};
If I do:
this.message2 = function() {
var = temp;
$http({
url : 'dataset_of_model',
method : 'POST',
data : {
'experiment' : 'rcp85',
'model' : 'HadGEM2-ES',
'frequency' : 'day'
}
}).then(function(response) {
console.log('qui');
console.log(response.data);
temp = response.data;
}, function(response) {
//fail case
console.log(response);
console.log(fallito);
return response;
});
return temp;
};
The return temp doesn't have the data because it returns before data, even if I wait for example 10 seconds before return.
How can I return data in a synchronous way?
Thanks!!
Try this.
this.getResults = function(){
return $http.get(url).then(function(res){
//Resquest successs
}, function(err){
//Resquest fail
});
}
And then
MyFooService.getResults().then(...);
I'm not sure why would would want to do that? You're returning response.data anyways in the first option. You should also probably add return before the $http (that way you're returning the promise). However, anything that you want to happen synchronized with the response must be in the .then block. For example, if you wanted to get a response of all goals scored, then add that number to some variable, you'd have to put that functionality in the .then block, then return the response.data at the end of the block.
That way, the functions executed inside the then block are synchronous.
You have no guarantee over the functions executed outside the then block, unless you want to hack it with $timeout (stop for 2 secs, then execute that return temp), which I don't recommend.

Force rejecting Angular $http call

I am using $http to make a call. Based on the successful result of the call I may decided to throw an error/reject and have it trickle down to the next call as an error. However if an error is thrown it just halt the process. How can I force the $http promise to reject without wrapping it in some $q code?
// A service
angular.module('app').factory('aService', function ($http, config) {
return {
subscribe: function (params) {
return $http({
url: '...'
method: 'JSONP'
}).then(function (res) {
// This is a successful http call but may be a failure as far as I am concerned so I want the calling code to treat it so.
if (res.data.result === 'error') throw new Error('Big Errror')
}, function (err) {
return err
})
}
}
})
// Controller
aService.subscribe({
'email': '...'
}).then(function (result) {
}, function (result) {
// I want this to be the Big Error message. How do I get here from the success call above?
})
In the above code I would like the Big Error message to end up as a rejected call. However in this case it just dies with the error. This is how I handle things in say Bluebird but it's a no go here.
Ti continue the Chain in a rejected state just return a rejected promise $q.reject('reason') from your $http result something like
$http.get(url).then(
function (response){
if(something){
return $q.reject('reason');
}
return response;
}
)
That way you'll get a a rejected promise and can react to it even when the api call is successful.

Get result of rest request instead of response?

I have the following function in my controller.
RestRequestsSrvc.getUserDetail()
.then(
function (response) {
$scope.user.userDetail = response;
},
function (error) {
// TODO
});
If I type
console.log(RestRequestsSrvc.getUserDetail());
the console logs a promise. I want to set a variable the the response. How can I modify my code so that I get the response instead of a promise?
Return a promise because your request is async.
You should wait the response,
Putting the console.log inside the callback function should print your info.
RestRequestsSrvc.getUserDetail()
.then(
function (response) {
$scope.user.userDetail = response;
console.log(response);
},
function (error) {
// TODO
});
You can do the console.log into the promise .then
RestRequestsSrvc.getUserDetail()
.then(
function (response) {
$scope.user.userDetail = response;
console.log(response);
},
function (error) {
// TODO
});
The thing is that when you call the function it will be executed but will not wait for the result, that's why you get a promise. The .then stuff is called once the request is done executing. That's where you handle your success or error callbacks.
Requests to the server are asynchronous, meaning that you must handle the response inside the callback.
You could use async false flag but this is not recommended if you have independent modules that executed later in the code.

$http and promises and POST

Given code like the following:
function webCall() {
return $http({ method: "POST",
url: "http://destined/to/fail", data: {param1: 1})
.success(function(data, status) { return { test: "success!";} } )
.error(function (data, status) {
return {errorMessage: "Totally failed!"};
});
It is my understanding that if I call .then() on the returned promise like so:
var myPromise = webCall().then(
function(response){
console.log(response.test);
},
function(reason) {
console.log(reason.errorMessage);
});
that the returned value from the appropriate .success() and .error() callbacks is passed along to the .then() callbacks.
I am not seeing the behavior I expect however. Using GET it works as expected. With POST, not so much. Is my assumption that it should act like normal deferred \ promise accurate? Where is it documented (other than the source)
$http() returns a promise, but what you're returning is actually the result of .error(), which isn't a promise (at least it's not documented as such).
To return a promise, you have use then()...
function webCall() {
return $http({ method: "POST",
url: "/destined/to/fail", data: {param1: 1})
.then(function(response) {
// if you need to access the data here, use response.data
return { test: "success!" };
}, function (response) {
throw { errorMessage: "Totally failed!" };
});
}
Note the use of throw to reject the returned promise, because if you simply return from the error callback, it actually resolves resulting promise.
The reason it doesn't work as you expect is because the .success and .error handlers return the original promise and it does nothing with the return value from the success/error handlers. You can see that from the source code for $http here.
promise.success = function(fn) {
promise.then(function(response) {
fn(response.data, response.status, response.headers, config);
});
return promise;
};
You can see that once the promise is resolved (when the web call returns), it will call your sucess handler, and then do nothing with it.
When you chain this with other handlers it is the result of the original promise that is passed along to the other handlers.

angular jsonp factory doesn't work

I have this factory in angular, and its returning me a 0 error code always, even tho i can see it fetches from the json origin, can someone help me?
app.factory('docFactory', function($http) {
var docFactory = {
async: function(page) {
var url = 'http://example.com/ProviderRequest?&queryString=searchv2&callback=JSON_CALLBACK';
var promise = $http.jsonp(url).error(function (response, status) {
alert(status);
}).success(function (response, status) {
alert(status);
}).then(function (response, status) {
return response.data;
});
return promise;
}};
return docFactory;
});
I ran into this myself. If your JSONP is a function call or something falsy (null, false, undefined) you will encounter this behavior. Take a look at this code from AngularJS HTTP backend for more info (lines 41 - 54 are relevant).
If you are returning a function, you might just need to return true or something after the call.
Edit: After looking at your plunker it seems that your JSONP response is not calling the callback method. The response should be angular.callbacks._0( { ... } ); where { ... } is your object, and angular.callbacks._0 is the value of the callback query parameter in the request.
Your example above is almost right. Just change the callback parameter to
jsoncallback=JSON_CALLBACK
You should have something like that at the end
$http.jsonp(url + '?jsoncallback=JSON_CALLBACK').success(function(data) {
console.log(data);
})

Resources