This question already has answers here:
Is this a "Deferred Antipattern"?
(3 answers)
Closed 5 years ago.
I create $http service and using $q.
Here is my $http service:
function dashboardService($http, $log, $q, config) {
var service = {
getClientId: getClientIDByLayout,
};
return service;
function getClientIDByLayout(layoutId) {
var deferred = $q.defer();
return $http.get(config.baseUrl + "api/ClientLayoutMercator?layoutId=" + layoutId).then(function (result) {
deferred.resolve(result.data);
}, function (result) {
deferred.reject(result);
});
return deferred.promise;
}
}
And here is how I call service above inside controller:
dashboardService.getClientId(layoutId).then(function (data) {
var t = data;//undifined
});
But result I get in this row var t = data is undefined.
Any idea why I get undefined from the service?
Basically you have two return statement inside your getClientIDByLayout function and both are returning promise itself. As I can see with your current implementation your're creating new promise & managing rejection / resolve manually. But the problem is the 1st return statement (return $http.get() is making other return statement(return deferred.promise) redundant. Hence 1st promise returned to subscription from controller. Eventually $http.get doesn't return anything so you get undefined in successCallback of then.
You can easily fix this issue by removing 1st return statement as shown below.
function getClientIDByLayout(layoutId) {
var deferred = $q.defer();
//removed `return` from below code.
$http.get(config.baseUrl + "api/ClientLayoutMercator?layoutId=" + layoutId).then(function (result) {
deferred.resolve(result.data);
}, function (result) {
deferred.reject(result);
});
//custom promise should get return
return deferred.promise;
}
Ideally creating promise overhead considered as antipattern, rather you can utilize the promise returned by $http.get. Just return a data from its success callback to chain the promise.
Code
function getClientIDByLayout(layoutId) {
̶v̶a̶r̶ ̶d̶e̶f̶e̶r̶r̶e̶d̶ ̶=̶ ̶$̶q̶.̶d̶e̶f̶e̶r̶(̶)̶;̶
return $http.get(config.baseUrl + "api/ClientLayoutMercator?layoutId=" + layoutId)
.then(function (result) {
//returning data from promise, it will provide it to subsequent `.then`
return result.data;
}, function (error) {
͟r͟e͟t͟u͟r͟n͟ $q.reject(error);
}
);
}
Instead of using $q.defer, simply return or throw to the handler functions in the .then method:
function dashboardService($http, $log, ̶$̶q̶,̶ config) {
var service = {
getClientId: getClientIDByLayout,
};
return service;
function getClientIDByLayout(layoutId) {
̶v̶a̶r̶ ̶d̶e̶f̶e̶r̶r̶e̶d̶ ̶=̶ ̶$̶q̶.̶d̶e̶f̶e̶r̶(̶)̶;̶
return $http.get(config.baseUrl + "api/ClientLayoutMercator?layoutId=" + layoutId).then(function (result) {
̶d̶e̶f̶e̶r̶r̶e̶d̶.̶r̶e̶s̶o̶l̶v̶e̶(̶r̶e̶s̶u̶l̶t̶.̶d̶a̶t̶a̶)̶;̶
return result.data;
}, function (result) {
̶d̶e̶f̶e̶r̶r̶e̶d̶.̶r̶e̶j̶e̶c̶t̶(̶r̶e̶s̶u̶l̶t̶)̶;̶
throw result;
});
̶r̶e̶t̶u̶r̶n̶ ̶d̶e̶f̶e̶r̶r̶e̶d̶.̶p̶r̶o̶m̶i̶s̶e̶;̶
}
}
The .then method returns a new promise which is resolved or rejected via the return value of the successCallback or errorCallback (unless that value is a promise, in which case it is resolved with the value which is resolved in that promise using promise chaining).1
By erroneously returning a promise with a then method that contained functions that lacked return or throw statements the service was returning a promise that resolved as undefined.
For more information, see You're Missing the Point of Promises.
Related
This question already has answers here:
How do I return the response from an asynchronous call?
(41 answers)
Closed 4 years ago.
In AngularJS v1.6.9 I use HTTP post method for calling my API but $$state give me null so I am not able to give my list. I try to debug result = response.data give me to list but when that API Call service but post API function return also the list but in a controller, I enable to fetch that data.Please help me with that
Module is
var adminDashbord = angular
.module('mymodel', ['ui.router', 'ui.bootstrap', 'datatables', 'datatables.buttons']);
My API Call Service is
mymodel.service('ApiCall', ['$http', function ($http) {
var result;
var retResult;
var data;
this.PostApiCall = function (controller, method, jData) {
jData = JSON.stringify(jData);
result = $http.post('http://localhost:2153/' + controller + '/' + method, jData).then(function onSuccess(response)
{
result = response.data;
})
.catch(function onError(response)
{
console.error("CustomError:" + response.data);
console.error("Status:" + response.status);
});
return result;
};
Post API in a controller like
mymodel.controller('productmasterController', ['$scope', 'ApiCall',
function ($scope, ApiCall) {
$scope.init = function ()
{
$scope.getAllproduct();
}
$scope.getAllproduct = function () {
var reqdata = null;
result = ApiCall.PostApiCall("Product", "SelectAllProduct", reqdata).then(function (response) {
$scope.getProduct = response.ProductList;
});
};
}]);
My issue was $$state give an empty result.
You have two options:
result = $http.post(url).then(function(response){
return response.data;
})
return result;
OR
return $http.post(url).then(function(response){
return response.data;
})
Either way you need to return an Http Promise, which $http.get request returns. If you want your promise contain any data, you also need to return the response inside it, hence the second return is used (for response.data).
I am trying to spy on an asynchronous service call, however so far not successful. Though the synchronus calls are able to Mock.
commonService.getResource(ResourceConst).then(function (result) {
$scope.resource = result.data;
}, function (error) {
loggerService.log(error);
});
Actual Get Resource Function
function getResource(resourceURL) {
var deferred = $q.defer();
var url = resourceURL + ".json";
$http.get(url).then(function (data) {
deferred.resolve(data);
},
function (error) {
deferred.reject(error);
});
return deferred.promise;
}
Even though I was able to spy on synchronus methods in the same service like below I was not able to do the same for this function call.
spyOn(commonService, 'hasData').and.callFake(function(args) {
return true;
});
The above code success fully mocked the synchronus call ,
$scope.hasData = commonService.hasData($scope.resource.Data);
How can I return a result
{'name': "Name"} to the asynchronous call getResource
Since the service is supposed to return a promise, return value should be mocked with a promise:
spyOn(commonService, 'hasData').and.returnValue($q.resolve({'name': "Name"} ));
The code above uses deferred antipattern. It should be instead:
function getResource(resourceURL) {
var url = resourceURL + ".json";
return $http.get(url);
}
I'm trying use $http.get() in a factory to retrieve a value which will be processed further in a controller.
appFac.factory('CompletedJobs',function($http){
var result;
return{
getJobs : function(webserviceurl){
$http.get(webserviceurl).success(function(data){
result = data;
})
.error(function(err){
result = '';
});
return result;
}
}
})
appCtrl.controller('Ctrl',function(CompletedJobs){
var data = CompletedJobs.getJobs('some url');
// some other logic based on data
});
Due to the asynchronism, by the time the data becomes available inside the factory method, it is of no use to the controller because the factory method has already returned undefined.
Can anyone help me work out how to get the data from the factory method so it can be used by the controller?
In your factory you should return the promise of success or error instead of explicitly returning the result because that will be returned before your promise get resolved. Modify you factory like this.
appFac.factory('CompletedJobs',function($http, $q){
return{
getJobs : function(webserviceurl){
var deferred = $q.defer();
$http.get(webserviceurl).then(function(response){
deferred.resolve(response.data);
return deferred.promise;
});
}
}
})
appCtrl.controller('Ctrl',function(CompletedJobs){
var data = CompletedJobs.getJobs('some url');
// some other logic based on data
});
This should work for you.
In the factory return the promise.
appFac.factory('CompletedJobs',function($http){
return {
getJobs : function(webserviceurl){
//return the promise
return $http.get(webserviceurl);
}
};
In the controller chain from the promise.
appCtrl.controller('Ctrl',function(CompletedJobs){
var promise = CompletedJobs.getJobs('some url');
promise.then( function onFulfilled(result) {
var data = result.data
// some other logic based on data
}).catch( function onRejection(result) {
console.log(result.status);
});
});
I built a factory to return data that uses an HTTP Get through a deferred promise. It work great when it is the happy path and the url is correct. But when there is an error I would like to catch it. It seems that I am but a 500 error still shows in the console. Is there a way to catch this also? Also, I want to do processing on the reject I'm having trouble figuring out how to do that. TIA
angular.module("accQueries")
.factory('leaseFactory', ['$http', '$q', function ($http, $q) {
return {
leases: '',
makeRequest: function (url) {
// Create the deferred object
var deferred = $q.defer();
$http.get(url).then(function (resp) {
deferred.resolve(resp.data);
})
// potentially catch http error here??
.catch(function (err) {
deferred.reject(err);
console.log('rejected : ' + err );
console.dir(err);
this.leases = '';
});
return deferred.promise;
},
// Return a single lease based on lease number
getLease: function (pLeaseNum) {
this.leases = this.makeRequest("http://someserver/AccruentQA_DB/webresources/restfulservices.latbllease/leaseNumber/" + pLeaseNum);
// Return the lease object stored on the service
return this.leases;
},
// Return all leases based on lease name
getLeases: function () {
this.leases = this.makeRequest("http://someserver/AccruentQA_DB/webresources/restfulservices.latbllease/name/");
// Return the lease object stored on the service
return this.leases;
}
};
}]);
It is not needed to wrap a $http call in $q, because $http returns a promise itself. So just returning $http like this is sufficient:
makeRequest: function (url) {
return $http.get(url);
}
If you would want to chain do something in the makeRequest function with the answers be4 passing it on, you can chain promises like so:
makeRequest: function (url) {
return $http.get(url).then(function(response){
//do something
return response;
}, function(error){
//do something
return error;
});
}
There's no way to prevent the HTTP error from appearing in the console. The browser does that before it passes the results back to angular. However, an error causes the $http promise to be rejected, which means you can handle it using the optional second argument to then()
return $http.get('url').then(
function(response) {
this.leases = response.data;
},
function(response) {
var statusCode = response.status;
var response = response.data;
// other error processing
this.leases = '';
}
}).then(function() { return this.leases; }
You can do various things depending on the status code and response data. If your server emits an error 500, that's what response.status will be. Timeouts have a status of 0.
You should also be aware that getLease() will return before the ajax request is complete. You should return the promise, and then in the calling code, chain another then() to do something once the promise is resolved.
Here is my service's response:
response = response.then(function (data) {
return data.data;
});
response.catch(function (data) {
$q.reject(data);
});
// Return the promise to the controller
return response;
In Interceptor I am returning:
return $q.reject();
But, still I am getting back into:
response.then
Is possible to get back into the catch block?
Thanks
Adding more code:
.service('APIInterceptor', function ($q, $rootScope, UserService) {
var service = this;
service.request = function(config) {
return $q.reject();
//return config;
};
service.responseError = function (response) {
return response;
};
})
What happens is that your .request creates an error (by doing return $q.reject()), but your .responseError "handles" that error (by virtue of being there), thus resulting in the overall successful resolution.
Indeed, removing .responseError handler makes the error bubble up to .catch. Alternatively, you can also return $q.reject() in .responseError.