Angular 1.6.4 $http post callback not called [duplicate] - javascript

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.

Related

Mixing jquery promise with Parse promises

I am using jquery to send an ajax request to perform certain tasks and based on the result of that jquery GET response I am performing some action on Parse Js library.
e.g.
I have created a method like this to send the request.
sendRequest(URL, userId){
return $.ajax({
url: URL + userId,
type: 'GET',
}).fail((responseData) => {
if (responseData.responseCode) {
console.error(responseData.responseCode);
}
});
}
I am using it like this -
sendRequest(URL, userId)
.then(
(data) => {
// Example
// Get some value from data and save it in Parse object
var GameScore = Parse.Object.extend("GameScore");
var gameScore = new GameScore();
gameScore.set("score", 1337);
gameScore.set("playerName", "Sean Plott");
gameScore.set("cheatMode", false);
return gameScore.save();
}
).then(
(changedGameObj) => {
console.log(changedGameObj);
// At this point receiving a parse promise which is not resolved yet.
},
(error) => {
}
);
I know that I am mixing jquery promise and Parse promise but don't know the solution of how to get resolved parse promises because jquery promises gets resolved earlier.
I am quite new to promise in js and please point me where I am wrong.
From what I can see your promise chain is this (removing functions, etc):
$.ajax().fail().then().then(success(), error());
You can see the documentation for jQuery's deferred for more details.
There's two things to try:
1) Convert jQuery deferred to a real promise. I'd do this because I don't care to learn jQuery's deferred. You may need to dig in here so...
Suggested by Bergi: return Parse.Promise.resolve(jqueryPromise)
OR
2) Chain your Parse promises together in the same then():
sendRequest(URL, userId) // This is jQuery deferred
.then( (data) => {
// ...
return gameScore.save() // This is a promise
.then( (changedGameObj) => { // success
console.log(changedGameObj);
}, (error) => { // error
console.warning(error);
}); // this whole promise is returned to the deferred.
})
.fail() // Back to jquery deferred.
.always()
.whatever()
Based on the Suggestion of Michael in first part I created a helper method to convert Jquery deferred to Parse promise. I feel that is much elegant way to do it.
convertJqueryToParsePromise(jqueryPromise) {
let promise = new Parse.Promise();
jqueryPromise
.then(
(results) => {
promise.resolve(results);
},
(error) => {
promise.reject(error);
}
);
return promise;
}
Usage:
convertJqueryToParsePromise(sendRequest(URL, dataToSend))
.then(
(success) => {
// Do something here
},
(error) => {
}
);

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 deferred.reject not working but $q.reject working

I am confused between Angular JS deferred and $q. I found this SO Question that explains the difference between $q.defer() and $q.It explains
$q.reject is a shortcut to create a deferred and then reject it immediately
So $q.reject() must be equal to
var deferred = $q.defer(); deferred.reject(), if not please explain the actual difference between the two.
But in my case, $q.reject() is working, but deffered.reject() is not working. Also we need to return rejected promised like $q.reject() but not deferred.reject(). I have seen examples where there is no return on deffered.reject()
This is the code
var deferred = $q.defer();
myService.getData()
.then(function(response){
deferred.notify('Just a notification');
deferred.reject('rejected');
})
.then(function(response) {
console.log('done');
}, function(response) {
console.log('rejected');
})
This is not working, but when I replaced deferred.reject with $q.reject(), the promise has been rejected and the control is moved to the error function of the followed then block.
Any help is greatly appreciated. Thanks in advance.
It doesn't work when you use deferred.reject because you are not returning new rejected promise. You can use both $q.reject() and deferred.reject() you just need to return a promise in both cases.
You need to to understand that
$q.reject() is rejected promise object
deferred.reject() is not a promise, but deferred object which has rejected promise in one of its properties (namely, $promise).
So you can return any object or value and it will become a new promise object and will be passed to the next then block in chain. However, when you return deferred.reject() it will be passed as just some object (one more time, it is not a promise, but it has a promise inside) and next promise will get resolved successfully of course.
It will work properly with deferred too if you return corresponding promise:
var deferred = $q.defer();
myService.getData()
.then(function(response) {
deferred.notify('Just a notification');
deferred.reject('rejected');
return deferred.promise;
//return $q.reject();
})
.then(function(response) {
console.log('done');
}, function(response) {
console.log('rejected');
});
And finally answer to you question: $q.reject() is a promise object with status "rejected". deferred.reject() is not a promise, but it has rejected promise object inside as deferred.$promise. What to use? You should use $q.reject(), using dummy deferred object is redundant in this case and considered bad practice, in fact it even has a name as deferred anti-pattern.
Make sure you are returning a promise.
function getData() {
var deferred = $q.defer();
myService.getData()
.then(function (response) {
deferred.resolve('Just received a notification');
}).catch(function (err) {
deferred.reject(err);
};
return deferred.promise;
}
getData().then(function (response) {
console.log('done');
}, function (response) {
console.log('rejected');
});
This is working with Q (https://github.com/kriskowal/q)
var def = Q.defer();
def.promise
.then(
function(ok){
return ok;
},
function(err){
var d = Q.defer();
d.reject(err);
return d.promise;
}
)
.then(
function(ok){
console.log('ok',ok);
},
function(err){
console.log('err',err);
}
);
def.reject('error');

Asynchronus calls in angularjs

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!

Angularjs promise rejection chaining

I need to create chained promises:
var deferred = $q.defer();
$timeout(function() {
deferred.reject({result: 'errror'});
}, 3000);
deferred.promise.then(angular.noop, function errorHandler(result) {
//some actions
return result;
}).then(function successCallback(result) {
console.log('what do I do here?');
return result;
}, function errorCallback(result) {
$scope.result= result;
return result;
});
If I put an errorCallback into the first then, the second then will be resolved and its successCallback will be called . But if I remove errorHandler then second promise will be rejected.
According to Angular JS docs the only way to propagate rejection is to return $q.reject(); and it looks not obvious, especially because I have to inject $q service even if it is not needed;
It can also be done by throwing an exception in errorHandler, but it writes exception trace to console, it is not good.
Is there another option to do this in a clear way? And what is the reason? Why it is done? In which case, the current behavior can be useful?
And what the reason why it is done. In which case, the current behavior can be useful?
It can be useful when in errorHandler you could try to repair error state and resolve promise somehow.
var retriesCount = 0;
function doWork()
{
return $http.post('url')
.then(function(response){
// check success-property of returned data
if(response.data.success)
// just unwrap data from response, may be do some other manipulations
return response.data;
else
// reject with error
return $q.reject('some error occured');
})
.catch(function(reason){
if(retriesCount++ < 3)
// some error, let me try to recover myself once again
return doWork();
else
// mission failed... finally reject
return $q.reject(reason);
});
}
doWork().then(console.log, console.error);
Late to the party, but as I am here;
I prefer to use the $http error for its native error handling, rather than returning a success via a 200 and an error status in the response.
printing 400 or 500 errors in the console is not an issue, if you are debugging you see them if not you don't.
angular.module('workModule', [])
// work provider handles all api calls to get work
.service('workProvider', ['$http', '$q', function($http, $q) {
var endpoint = '/api/v1/work/';
this.Get = function(){
// return the promise, and use 404, 500, etc for errors on the server
return $http.get(endpoint);
};
}])
.controller('workController', ['workProvider', function('workProvider'){
workProvider.Get().then(
function(response){ // success
console.log(response.data);
},
function(response){ // error
console.log(response.data);
}
)
}])

Categories

Resources