I am a bit confused about what the statement
return $q.reject(response);
does inside the responseError interceptor.
I have gone through this article on webdeveasy and one on angular docs but they haven't helped.
Here is my code (for reference):
(function () {
"use strict";
angular.module('xyz').factory('errorResponseInterceptor', ['$q', 'toaster', function ($q, toaster) {
return {
...
...
...
responseError: function (response) {
...
...
//some logic here
...
...
if (response.status == 500) {
toaster.error({ title: "", body: response.statusText });
return $q.reject(response);//what does this statement does and why do we need to put it here?
}
return response;
}
};
}]);
}());
My Question is:
Why do we need to write return $q.reject(response)?
How does that line affect the angular app (what does it do)?
The $q object in Angular allows us to define a promise and handle the behaviour associated with a promise. An example of how we might do this is:
var q = $q.defer()
//do something
if(success){
q.resolve()
} else {
q.reject()
}
return q
What we are doing here is defining a promise object and assigning it to the variable q. Then we perform some operation and use the result of that to determine whether the promise returns successfully or not. Calling .resolve() on the promise object is indicative of the fact that the promise has returned correctly. By the same token calling .reject() on the promise is indicative of the fact that is has failed.
If we consider how we actually use a promise:
SomeFactory.getSomething().then(function(data){
//gets called if promise resolves
}, function(error){
//gets called if promise rejected
}
So in the example you have provided, we are checking to see if the response has a 500 error code, and if it does, we are rejecting the promise thus allowing us to handle the error.
In a responseError interceptor you have two choices with regards to what you return. If you return $q.reject(response) you are continuing the error handling chain of events. If you return anything else (generally a new promise or a response) you are indicating that the error has been recovered from and should be treated as a success.
With regards to your two points:
1- You need to write that line to indicate that the response should still be considered an error.
2- The effect on the angular app is that the error callback will be called instead of the success callback.
Related
I am starting to learn about promises in Javascript and I am still not getting my head around it. The code below is mostly real code. I have placed several debugger statements so the program stops and I can understand how the flow works and inspect some variables. I have read some blog posts about promises and I still can't understand everything. This is from an app which uses AngularJS and q library.
Few questions:
1- What does deferred.Resolve() do exactly? What is it doing with response.data? When I inspected the 'deferred' object and its 'promise' object, I couldn't see any trace for response.data.
2- When I resumed execution after debugger #1, I thought the http post statement would run but execution jumped to the return statement. I guess that's where the promise jumped in and the post will happen in the future?
3- How do I know when the post will happen when the function returns? The caller will get the return promise, what is the caller expected to do with it?
this.GetData = function()
{
var data = blahblah;
var deferred = this.$q.defer();
debugger; //1
this.$http.post(someurl, data,
{
headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
handleErrors: false
})
.then(function(response) {
debugger; //2
(domesomething...)
deferred.resolve(response.data);
},
function(error) {
(logerror...)
deferred.reject(error);
});
debugger; //3
return deferred.promise;
};
It's appropriate to use q.defer() (an explicit creation of a promise) when wrapping callback style code, "promisifying"...
this.timeoutWithAPromise = function(msec) {
let defer = q.defer();
setTimeout(() => defer.resolve(), msec);
return defer.promise;
};
The foregoing says: "Create a promise and return it right away. That promise will be fulfilled when msec has passed.
Question 1: resolve() fulfills the promise, calling whatever function that's been set with then(), passing it whatever parameter was passed to resolve.
Question 2: You're right, the async operation is begun and a promise for it's completion is returned right away.
Question 3a: The post will begin (or the timeout will commence in my e.g.) on another thread as soon as that invocation is made. It will continue concurrently with execution on this calling thread, until it completes, which you understand from Question 1, is done by resolving the promise, invoking it's then().
Question 3b: What is the caller to do with a returned promise? Attach a block to it that you wish to run upon completion, possibly additional async actions. Taking my example...
let self = this;
self.timeoutWithAPromise(1000).then(()=> {
console.log('1000msec have passed');
return self.timeoutWithAPromise(1000);
}).then(()=> {
console.log('now, another 1000msec have passed');
// ...
Rewriting your example, realizing that $http already conforms to promises...
var data = blahblah;
let headers = { 'Content-Type': 'application/x-www-form-urlencoded' };
let config = { headers: headers, handleErrors: false };
let httpPromise = this.$http.post(someurl, data, config).then((response)=> {
console.log('got a response. tell our caller about it');
return response.data;
}).catch((error)=>
console.log('got an error. either handle it here or throw it');
throw error;
});
// this is important: return the httpPromise so callers can chain off of it
return httpPromise;
Now a caller can say:
let promiseToDoEvenMore = this.GetData().then((data)=> {
console.log(data);
return this.GetMoreData(); // etc.
});
return promiseToDoEvenMore; // and so on
I have the below function, which checks if the user has entered the right password and username. This functions correctly however the function returns before the success/error functions are fired.
Therefore the function always returns true, what would be the best method to have the function return the right value.
angular.module('services.security', [])
.provider('securityService', function() {
this.$get = function ($q, $location, $rootScope, $window, $injector) {
...
loginCheck: function(username, password) {
var failed = true;
$injector.get('$http').post(AUTH_URL,
{username: username, password: password}, {loginType: "cancel"}
).success(function loginSuccess(data) {
failed = false;
}).error(function loginFailure(data, status) {
failed = true;
});
return failed;
},
You would want to return the promise.
So you can just return your http-call:
return $http("...");
In your code you can then just use this returned promise to check if it was succssful:
loginCheck(u, p).then(function() {
//success
}, function() {
// error
});
You should do some research into JavaScript Promises. That's what you're working with here. The problem is that HTTP calls are (generally) asynchronous, meaning that the code doesn't wait for them to finish before continuing. This is where promises come in.
Instead of returning the true/false value directly, your function should instead return a promise representing the true/false value. You can then attach handler functions to it that essentially say "whenever the HTTP call finishes, pass the result into these functions. If you're using a more recent version of Angular, e.g. 1.4.4+ or 1.5.x (not sure about Angular 2, but probably this will still apply), the $http.post() method returns a promise anyway, so you can return that directly.
The key thing is the .then() method of promises. This takes 2 arguments: the first argument is a success function, the second is a failure function. So you should just rewrite your code like this:
loginCheck: function(username, password) {
var failed = true;
return $injector.get('$http').post(AUTH_URL,
{username: username, password: password}, {loginType: "cancel"}
);
}
// ...
// somwhere else where securityService.loginCheck() is used
securityService.loginCheck(username, password).then(
// success
function(data) {
// handle successful login
},
// failure
function(data, status) {
// handle failed login
}
);
This is how asynchronous code is written these days. Definitely read up on Promises and asynchronous concepts i general, it's super important in modern web development.
An introduction to promises
Promises A+ standard
MDN's documentation on the recent built-in Promise type
$q, Angular's promise service
Would anyone know how to return the result of a Promise from a cloud code module? I am using the examples here but it keeps telling me that options is undefined (or nothing if I check with if(options) first.
I am calling the function with module.function as a promise, but still not getting results.
And ideas?
Edit: I can FORCE it to work but calling:
module.function({},{
success:function(res){
//do something
},
error:function(err){
//handle error
}
})
but this isn't really great since 1) I have to stick the empty object in there 2) I can't force the object to work like a promise and therefore lose my ability to chain.
Not sure if the issue is with modules or promises. Here's some code that illustrates both, creating a module with a function that returns a promise, then calling that from a cloud function.
Create a module like this:
// in 'cloud/somemodule.js'
// return a promise to find instances of SomeObject
exports.someFunction = function(someValue) {
var query = new Parse.Query("SomeObject");
query.equalTo("someProperty", someValue);
return query.find();
};
Include the module by requiring it:
// in 'cloud/main.js'
var SomeModule = require('cloud/somemodule.js');
Parse.Cloud.define("useTheModule", function(request, response) {
var value = request.params.value;
// use the module by mentioning it
// the promise returned by someFunction can be chained with .then()
SomeModule.someFunction(value).then(function(result) {
response.success(result);
}, function(error) {
response.error(error);
});
});
In angular code, I have a chained promise like this:
// a function in LoaderService module.
var ensureTypesLoaded= function(){
return loadContainerTypes($scope).then(loadSampleTypes($scope)).then(loadProjectList($scope)).then(loadSubjectTypes($scope));
}
Each of these functions return a promise, that loads things from a resource and additionally modifies $scope on error and success, e.g.:
var loadProjectList = function ($scope) {
// getAll calls inside a resource method and returns promise.
return ProjectService.getAll().then(
function (items) {
// succesfull load
console.log("Promise 1 resolved");
$scope.projectList = items;
}, function () {
// Error happened
CommonService.setStatus($scope, 'Error!');
});
};
I intent to use in in a code in a controller initialziation as such:
// Part of page's controller initialization code
LoaderService.ensureTypesLoaded($scope).then(function () {
// do something to scope when successes
console.log("I will do something here after all promises resolve");
}, function () {
// do something when error
});
However, this does not work as I'd like to. Ideally message "I will do something here after all promises resolve" must appear after all promises resolve. Instead, I can see that it appears earlier than messages from resolved promises within functions that are listed ensureTypesLoaded.
I would like to create a function ensureTypesLoaded such, that:
it returns a promise that is resolved when all chained loads are resolved;
if any of "internal" promises fail, the function should not proceed to next call, but return rejected promise.
obviously, if I call ensureTypesLoaded().then(...), the things in then() must be called after everything inside ensureTypesLoaded is resolved.
Please help me with correct building of chained promises.
I thinks that problem is in your loadProjectList function. Because .then() should receive function which is called at resultion time. Usually is function returning chain promise.
But in your case you call all load immediately in parallel. It's little complicated with $scope passing. But I think your code should appear like this
//this fn is called immediatly on chain creation
var loadProjectList = function ($scope) {
//this fn is called when previous promise resolves
return function(data) {
//don't add error handling here
ProjectService.getAll().then(...)
}
}
This cause serial loading as you probably want. (just notice: for parallel excution in correct way $q.all is used)
Finally you should have error handler only in ensureTypesLoaded, not in each promise.
I have read up on Kris Kowal's Q and angularjs $q variable for a few hours now. But for the life of me I can't figure out how this works.
At the moment I have this code in my service:
resetpassword: function (email, oldPassword, newPassword) {
var deferred = $q.defer(); //Why am I doing this?
var promise = auth.$changePassword(email, oldPassword, newPassword); //$changepassword in angularfire returns a promise
console.log(deferred); //Object with resolve, reject, notify and promise attrs
var rValue = promise.then(function(){
//successcallback
return 1; //if changepassword succeeds it goes here as expected
}, function(error){
//errorcallback
return error.code; //if changepassword fails (wrong oldpass for example) it goes here, also works
}
);
deferred.resolve(promise); //Should I do this? I thought my promise was resolved in the callbacks?
console.log(rValue); //This outputs another promise? Why not 1 or error.code, how the am I ever going to get these values??
return rValue; //I want 1 or error.code to go back to my controller
},
var deferred = $q.defer(); //Why am I doing this?
It's the deferred anti-pattern that you are doing because you don't understand promises. There is literally never a good reason to use $q.defer() in application logic.
You need to return a promise from your function:
resetpassword: function(email, oldPassword, newPassword) {
return auth.$changePassword(email, oldPassword, newPassword)
.then(function() { return 1; })
.catch(function(e) { return e.code });
}
Usage is:
resetpassword(...)
.then(function(num) {
if (num === 1) {
}
else {
}
});
It helps to think how the function would be written in synchronous code:
resetpassword: function(email, oldPassword, newPassword) {
try {
auth.$changePassword(email, oldPassword, newPassword)
return 1;
}
catch (e) {
return e.code;
}
}
A promise is a function that does not execute immediately. Instead, you are 'promising' to respond to the status of a deferred object whenever it has completed it's processing. Essentially, you are creating an execution chain that simulates multiple threads. As soon as a deferred object is complete, it returns a status code, the calling function is able to respond to the status code, and return it's own status code if necessary, etc.
var deferred = $q.defer();
Here you are creating a deferred object, to hold the callbacks which will be executed later.
var promise = auth.$changePassword(email, oldPassword, newPassword);
This is the actual function, but you are not calling it here. You are making a variable to reference it, so that you can monitor it.
var rValue = promise.then(function(){
The .then() is the function that will be executed if the promise is successful. Here you are inlining a function that will execute upon the success of the promise's work.
}, function(error){
And then chaining the error function, which is to be executed if the promise fails. You are not actually doing anything here other than returning the error code, but you could do something to respond to the failure yourself.
deferred.resolve(promise);
This is the point where you are actually telling your deferred object to execute the code, wait for a result, then execute either the success or the failure code as appropriate. The promise is added to the execution chain, but your code is now free to continue without waiting and hanging up. As soon as the promise is finished and a success or failure is processed, one of your callback functions will process.
console.log(rValue);
Your function is not returning the actual values here because you don't want your function to stop and wait for a response before continuing, since this would block the program until the function completes.
return rValue;
You are returning your promise to the caller. Your function is a promise which is calling a promise which might be calling another promise, etc. if your function was not a promise, then even though the code you are calling is not blocking, your code would become a block as you wait for the execution to complete. By chaining promises together, you are able to define how you should respond without responding at exactly that moment. You are not returning the result directly, but instead 'promising' to return a success or fail, in this case passing through the success or fail from the function you are calling.
You use deferred promises when you are going to perform a task that will take an unknown amount of time, or you don't care when it gets done - an Asynchronous task. All you care about is getting a result back whenever the code you called tells you that the task is completed.
What you should be doing in your function is returning a promise on the deferred that you are creating. Your code that calls resetpassword should then wait for this promise to be resolved.
function (email, oldPassword, newPassword) {
var deferred = $q.defer(); //You are doing this to tell your calling code when the $changepassword is done. You could also return the promise directly from angularfire if you like but of course the return values would be different.
var promise = auth.$changePassword(email, oldPassword, newPassword); //$changepassword in angularfire returns a promise
console.log(deferred); //Object with resolve, reject, notify and promise attrs
promise.then(function(){
//successcallback
deferred.resolve(1); //if changepassword succeeds it goes here as expected
}, function(error){
//errorcallback
deferred.reject(0); //if changepassword fails (wrong oldpass for example) it goes here, also works
}
);
// Quite right, you should not do this.
console.log(rValue); //This outputs another promise? Why not 1 or error.code, how the am I ever going to get these values??
return deferred.promise; // Return the promise to your controller
}
What you have done is mixed promises. As you put in the code comments,
$changepassword in angularfire returns a promise
Basically, var deferred = $q.defer() creates a new promise using deferred.promise, but you are already creating a promise with $changePassword. If I understand what you are trying to do correctly, you would simply need to do this.
resetpassword: function (email, oldPassword, newPassword) {
auth.$changePassword(email, oldPassword, newPassword).then(function(result) {
console.log(result); // success here
}, function(err){
console.log(err); // error here
});
}
EDIT:
Upon Robins comment, if you want to deal with the result in a controller, rather than call the then() function inside the service, return the promise itself to the controller. Here is an example:
Service function:
resetpassword: function (email, oldPassword, newPassword) {
// return the promise to the caller
return auth.$changePassword(email, oldPassword, newPassword);
}
Controller function:
.controller('MyCtrl', ['$scope', 'myService', function($scope, myService){
// get the promise from the service
myService.resetPassword($scope.email, $scope.oldPassword, $scope.newPassword).then(function(result){
// use 1 here
}, function(err){
// use err here
});
}]);