AngularJS finally not executed if promise is notify - angularjs

In my AngularJS application I'm trying to use finally in $http call.
This is my service
app.service("webResource", function($http, $q) {
return {
post : function(url, json) {
var defer = $q.defer();
$http.post(url, json)
.success(function (data, status) {
if (data!=null) {
defer.resolve(data);
} else {
defer.notify("Send notify....");
}
}).error(function (data, status) {
defer.reject({"response":data, "status": status});
});
return defer.promise;
}
};
});
In my controller I have
$scope.callServer = function() {
var promise = webResource.post('someurl',$scope.data);
promise
.then(
function(data) {
alert("Success");
//Do for success
},
function(data) {
alert("Error");
//Do for failure
},
function(data) {
alert("Notify");
}
).finally(function() {
alert("Finally");
});
};
If it's resolved or rejected, it's working fine. But if it's notify it alerts "Notify", but then not alerting "Finally". Why is that?
I'm using AngularJS 1.4.2 version

.finally takes two callbacks in the following form:
.finally(callback, notifyCallback)
You are currently only passing a single callback to your finally handler, as such you will only trigger the alert('Finally!) on either resolve or reject.
Add a second callback to the .finally step and you should be seeing a callback triggered when you do deferred.notify.
docs ($q.promiseAPI)

The reason it doesn't go to finally is because you can have multiple notifies. From the docs:
"notify(value) - provides updates on the status of the promise's execution. This may be called multiple times before the promise is either resolved or rejected."
Finally is once the promise is completely finished (both resolve and reject finish the promise)
https://docs.angularjs.org/api/ng/service/$q#the-deferred-api

Related

Return chain of promises in AngularJS [duplicate]

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.

Error handling in Angularjs for deferred promise

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.

AngularJS Event - dom is delayed updating

In my app, I have an event that listens for new messages sent to a user. Upon receiving the event, it runs a factory function to retrieve messages. However, it seems as though it is always 1 event behind (ie, event 1 data doesn't display until event 2 occurs).
I have a feeling this has to do with the digest cycle. I have tried $scope.$apply, $timeout to no avail. Hopefully I have been clear enough.
$scope.retrieveMessages = function(){
Conversations.retrieveConversations($scope.authentication.uid)
.then(function(success){
$scope.messageList = success;
}, function(error){
console.log(error);
});
};
$scope.$on('$RECEIVED_MESSAGE', function (event, data) {
$scope.retrieveMessages();
$scope.$apply();
});
Service
angular
.module('conversations')
.factory('EventEmitter', ['$rootScope',
function($rootScope) {
var factory = {
newMessage: function() {
$rootScope.$broadcast('$RECEIVED_MESSAGE');
}
};
return factory;
}]);
Function in controller that watches firebase for changes
var notificationsRef = new Firebase(config.firebaseRef + 'notifications/' + $scope.authentication.uid);
notificationsRef.limitToLast(1).on('child_added', function(childSnapshot, prevChildKey) {
var snapshot = childSnapshot.val();
if(snapshot.type === 'Conversation'){
EventEmitter.newMessage();
};
})
.catch(function(error) {
console.error("Error:", error);
});
Conversations Factory (omitted definition and other methods for brevity)
retrieveConversations: function(uid){
var deferred = $q.defer();
var request = {
uid: uid
};
$http.post(config.serverRef + '/conversations', request)
.success(function(data, status, headers, config) {
// this callback will be called asynchronously
// when the response is available
deferred.resolve(data);
})
.error(function(data, status, headers, config) {
// called asynchronously if an error occurs
// or server returns response with an error status.
deferred.reject(status);
});
return deferred.promise;
},
Issue was not with the code but timing and execution. Calls were happening faster than the re-indexing of firebase data to elasticsearch. Solved with $timeout(function(){$scope.retrieveMessages()}, 1000).

Angular promise - provide catch function

I am trying to come up with a generic service that would run my http requests and provides placeholders for success and error functions. Here is my code:
var deferred = $q.defer();
var response = $http({
..............
});
response.then(function (data) {
deferred.resolve(data.data);
});
response.catch(function (data) {
alert('Error');
});
And in Controller:
service.executeHTTPCall('parameters').then(successHandler);
My question is how do I provide an error handler for an executeHTTPCall call?
Thanks
Well, avoid the explicit construction and things fall into place:
// no deferred, just return
return $http({..... }).then(function(data){
// handle successes
return data.data;
}, function(err){ // handle failures here
alert("ERrror!!!"); // logic here
return $q.reject(err); // this is important, like a `catch and throw`
// to signal we're still handling errors.
});
But, may I suggest http interceptors instead?
$httpProvider.interceptors.push(function($q, dependency1, dependency2) {
return {
'response': function(response) {
// do something on success
},
'responseError': function(rejection) {
// do something on failure, see docs for more usage examples here
};
});
That would mean you can avoid wrapping every $http promise manually and also play nicer with tests.

AngularJs : $scope is in $apply phase ,still $http request is not sending immediately?

I am sending Http request . But its not immediately fire.And i already checked that its scope is in $apply phase. So any one have idea that Why this one is happening??
My service like:
changeUserRole : function (groupId,roleObj) {
var deferred = $q.defer();
$http.post("groups/" + groupId , roleObj)
.success(function (result) {
deferred.resolve(result);
}).error(function (error) {
deferred.reject(error);
})
return deferred.promise;
},
`
I try to debug it , And in that its go on $http.post("") line And then on return deferred.promise, But not fire any request.
Thank You in advance!!

Resources