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.
Related
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;
});
What is the use of $q.defer() in following code. I am bit confused or not able to understand the use of $q or defer?
service.serviceCall = function (methodName, params) {
var deferred = $q.defer();
$http({ method: "POST", url: url + methodName, data: params, headers: headers }).success(function (result) {
deferred.resolve(result);
}).error(function (result) {
deferred.reject(result);
});
return deferred.promise;
}
service.serviceCal("POST", {"param1":"value1"}).then(function(data){
//here data will be object which is resolved in success call
}).fail(function(){
//here data will be object which is rejected in failure call
})
This are promises, we user $q.defer() to return a promise using defer.promise(). It is contract between the calling object and promise that in future the calling object (here then and fail) will ultimately get a value either in then or fail depends whether it is resolved or rejected.
Read about promises here: Promises
Go through docs of $q module: $q Module Angular
A service that helps you run functions asynchronously, and use their
return values (or exceptions) when they are done processing.
So promises are used in async programming. $q is Angular's implementation of promises.
Usage of the above function:
service.serviceCal(methodName, params)
.then(function(resolve){
// on successful resolving
// called when defer.resolve is called
// resolved object: resolve
}, function(reject){
// on reject
// called when defer.reject is called
// rejected object: reject
})
The code is using deprecated methods on the result of calling $http. Although $http returns a promise it has some additional methods .success and .error that don't quite work within the usual promise structure.
$q.defer() creates a promise which is returned from the function and this code uses success and error to resolve the promise. A simpler way to write this code would be to just use the promises returned by $http and its .then method.
service.serviceCall = function (methodName, params) {
return $http({ method: "POST", url: url + methodName, data: params, headers: headers })
.then(function successFn (response) {
return response.data;
});
}
This has the same effect as the original code: it returns a promise which either resolves to the data from the response, or if an error occurs the promise is rejected.
I have read a lot of posts on promises,resolving promises, and accessing the data however I cannot seem to. Following some posts on Stack Overflow has just caused errors, so I am not sure what exactly I am doing wrong.
I have a function like so:
function getJsonl() {
var deferred = $q.defer();
$http({
url: 'urlNotShownForSecurity',
dataType:"json",
method: 'GET',
data:{"requestId":"123"},
headers:{"Content-Type":"application/json","requestId":"123"},
}).success(function(data) {
deferred.resolve(data);
console.log(data)
}).error(function(error,status,headers,config) {
deferred.reject(error);
});
return Promise.resolve(deferred.promise);
}
Here I return a json promise that has been resolved resulting in a json object I believe.
Printing to console I get the following:
Inside data is the information I need, it looks like this:
data:Array[8]
0:Object
description:"My description paragraph"
I have tried things with the returned object in my controller like:
vm.result = data.data[0].description;
vm.result = data[0].description
I have tried many different approaches in the view as well to access but I get 2 blank li tags and that is it.
I would like to be able to access the data so I populate a table. So if I can use it with ng repeat that would be great, as well as being able to access without because some data is used in more than just the table.
Update
#DanKing, following your implementation I get the following output in console:
Now I am back with a promise object.
It looks to me as though you're fundamentally misunderstanding the nature of promises.
$http() is an asynchronous function - that means it doesn't complete straight away, which is why it returns a promise.
It looks to me as though you're trying to call $http() and then get the result back and return it from your getJson1() method, before $http() has finished executing.
You can't do that. Your getJson1() method should just return the promise, so your calling method can chain onto it - like this:
getJson1().then(function(data) {
// do some other stuff with the data
});
The whole point of promise chains is that they don't execute straightaway - instead you provide callback functions that will be executed at some indeterminate point in the future, when the previous operation completes.
Your getJson1() function just needs to do this:
return $http({
url: 'urlNotShownForSecurity',
dataType:"json",
method: 'GET',
data:{"requestId":"123"},
headers:{"Content-Type":"application/json","requestId":"123"},
});
getJsonl().then(function(data){
console.log(data);
},function(err){
console.log(err);
})
should work. Where is your $http request and where is your call to getJsonl() will also make a difference. So choose that carefully when implementation. If you are using this in a service then you will have to return the function result say
this.somefunction = function (){
return getJonl();
}
and in your controller inject the service and do the following
service.somefunction().then(function(data){
console.log(data);
},function(err){
console.log(err);
})
Ok, rewrote the answer as a complete component to show the moving parts.
$http returns a promise so your original getJsonl call can be simplified. Using your original $http parameters this should dump your API response to the screen if you use the <json-thing> tag:
angular.module('yourModuleName')
.component('jsonThing', {
template: '<pre>{{$ctrl.thing|json}}</pre>',
controller: function ($http) {
var $ctrl = this;
getJsonl()
.then(function (response) {
console.log(response); // we have the $http result here
$ctrl.thing = response.data; // allow the template access
}, function (error) {
console.log(error); // api call failed
});
// call backend server and return a promise of incoming data
function getJsonl() {
return $http({
url: 'urlNotShownForSecurity',
dataType: 'json',
method: 'GET',
data: { requestId: '123'},
headers: { 'Content-Type': 'application/json', requestId: '123'}
});
}
}
});
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.
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);
})