Angular, Binding doesn't work when use Promise.catch - javascript

I use Auth0 for signin and I have the following code:
$scope.login = function () {
$scope.loginFailed = false;
$scope.loading = true;
loaderService.show();
let credentials = {
email: $scope.email,
password: $scope.password
};
principal.signin(credentials)
.then(() => {
$state.go('main.index');
})
.catch(e => {
showError(e.error_description);
loaderService.hide();
});
};
The principle service contains signin function:
signin(credentials) {
return new Promise((resolve, reject) => {
this.auth.signin({
connection: 'Username-Password-Authentication',
email: credentials.email,
sso: false,
password: credentials.password,
authParams: {
scope: 'openid name email'
}
}, this.onLoginSuccess.bind(this, resolve, reject), this.onLoginFailed.bind(this, reject));
});
}
So, as you can see I create promise and pass resolve/reject to Auth0 callbacks.
Callbacks are pretty simple:
onLoginSuccess(resolve, reject, profile, token) {
let userData = this.collectData(profile);
... store token
return this.syncCurrent(userData) //request to server
.then(() => {
return resolve();
})
.catch(() => {
this.signOut();
return reject();
});
}
onLoginFailed(reject, error) {
return reject(error.details);
}
So, let's return to the first snippet. There are the following code:
principal.signin(credentials)
.then(() => {
$state.go('main.index');
})
.catch(e => {
showError(e.error_description);
loaderService.hide();
});
When I use correct email/password redirect works fine and I see main page. But when I use wrong email/password I see that catch block is executed, I see that values are changed in the debugger, but I don't see error block and loading image is not disappered. This is not problem in html, because I am refactoring code now, and all the code above was in one file and I didn't use promises and all worked fine. I tried to execute showError method before principal.signin(credentials) function, just for test, and I saw error and loading image was hidden. So, I think the problem is with promises and catch block exactly, but I don't know where.
PS. The showError is the following:
function showError(errorText) {
$scope.loading = false;
$scope.loginFailed = true;
$scope.loginErrorMessage = errorText;
}

The cause of this problem is using non-Angular promises. Angular promises, i.e. the $q service, take care of invoking the digest cycle after they are resolved. The digest cycle is the implementation of change detection in Angular 1, i.e. what notifies watchers and enables actions to take place.
Using $q solves this problem.
The then part of your code probably worked because it called $state.go() which in turn invokes the digest cycle. The catch part did not, so the changes never got a chance to fire the watchers.

Related

Am I using .then() in the correct way?

Like the title says, it's clear based on running authHandler (see below) that it's not doing what I want it to. But I don't know why.
Basically, what I was wanting authHandle to do is first register a new user, generate a userUID and token, and subsequently update the state. Then, once that has been achieved, run the second half of the code to store the new user's user name.
Right now. All it does is registering the user (so initiating the first half of the code) but not executing the second .then() half.
I suspect it's something to do with the states between the first half and second half of authHandler.
I'm hoping someone can help me figure out where I went wrong ><
authHandler = () => {
return new Promise ((resolve, reject) => {
const authData = {
email: this.state.controls.email.value,
password: this.state.controls.password.value
};
this.props.onTryAuth(authData, this.state.authMode); // registers a new user and gives a userUID and token
})
.then(() => {
this.props.onAddUserData(
this.state.controls.userName.value,
)
}) //store the username of the new user
.catch(err => {
console.log(err);
alert("Oops! Something went wrong, please try again")
})
};
You need to either resolve or reject the promise you're creating - right now it's stuck in a pending state so whatever gets called in the then of authHandler() will never happen. You should also be returning the promise, calling then and catch where you do won't work properly. this code snippet reorganizes it in a way that should work.
authHandler = () => {
return new Promise ((resolve, reject) => {
try {
const authData = {
email: this.state.controls.email.value,
password: this.state.controls.password.value
};
this.props.onTryAuth(authData, this.state.authMode); // registers a new user and gives a userUID and token
this.props.onAddUserData(this.state.controls.userName.value)
resolve('done')
} //store the username of the new user
catch(err){
console.log(err);
alert("Oops! Something went wrong, please try again")
reject(err)
}
})
};

JS: Catch network error and return default response data

I am developing an app that uses promises to communicate with an remote API. This app needs to be able to work offline seamlessly, so I need to handle network errors. Since I can define some default data upfront that is good enough to keep the app functioning. My approach is to catch the error and return a new promise loaded with the default data:
API.js
function getDataFromAPI(id) {
return axios.get(`${BASE_URL}/${id}`)
.then(response => response.data)
.catch((error) => {
// Only return fake data in cases of connection issues
if (error.message == 'Network error') {
const fakeResponse = {myDefaultData: 'default data all over the place'};
// receiving function expects data in promise-form
return Promise.resolve(fakeResponse);
}
});
}
Action.js using the API
needSomeData = () => {
api.getDataFromAPI().then((response) => {
// Data is processed/used here
}));
The code sample works but I am not sure if this is a good/clean approach? Would it be better to handle this in a service worker? Or should I use an entirely different way to approach the issue?
so you can clean it a little bit more.
since any return from .catch consider the value of the next resolved promise. you do not need to return Promise.resolve(value) return value are enough
function getDataFromAPI(id) {
return axios.get(`${BASE_URL}/${id}`)
.then(response => response.data)
.catch((error) => {
// Only return fake data in cases of connection issues
if (error.message == 'Network error') {
return {
myDefaultData: 'default data all over the place'
};
else {
return 'return something or throw new exception'
}
});
}
So for whom that want to know exactly how Promise algorithm behave
Promises/A+ specification
In fact I find It very interesting

Promise hell, Anti-pattern and Error Handling

I am a newbie in node.js environment. I read a lot of source about implementing Promises and chaining them together. I am trying to avoid anti-pattern implementation but I could not figure it out how can I do it.
There is a User Registration flow in the system.
First, I check the username.
if there is no user in the DB with this username I create a User model and save it to the DB.
Can you kindly see my comment inline ?
app.js
RegisterUser("existUser","123").then(user=>{
//send response, etc
}).catch(er => {
console.log(er);
//log error,send proper response, etc
// Should I catch error here or inner scope where RegisterUser implemented ?
});
userService.js
function RegisterUser(username, password) {
return new Promise((resolve, reject) => {
GetUser(username)
.then(user=>{
if(user)reject(new Error("User Exists"));
else{
resolve(SaveUser(username,password))// Is that ugly?
//what if I have one more repository logic here ?
//callback train...
}
})
.then(user => {
resolve(user);//If I do not want to resolve anything what is the best practice for it, like void functions?
}).catch(err=>{
console.log(err); // I catch the db error what will I do now :)
reject(err);// It seems not good way to handle it, isn't it ?
// Upper promise will handle that too. But I dont know how can I deal with that.
});;
});
}
repository.js
function GetUser(username) {
return new Promise((resolve, reject) => {
if (username === "existUser")
resolve("existUser");
else resolve("");
});
}
function SaveUser(username, password) {
return new Promise((resolve, reject) => {
reject(new Error("There is a problem with db"));//assume we forgot to run mongod
});
}
The code above seems awful to me.
I thought I need to define some method that can chain after GetUser method.
like
GetUser(username)
.then(SaveUserRefined)// how could it know other parameters like password, etc
.then(user=> {resolve(user)}) // resolve inside then ? confusing.
.catch(er=>//...);
I feel I do anti-pattern here and create "promise hell"
How could a simple flow like that implemented.
Validate username and save it.
Thanks.
Yes, that's the Promise constructor antipattern! Just write
function registerUser(username, password) {
return getUser(username).then(user => {
if (user) throw new Error("User Exists");
else return saveUser(username,password)
});
}
Notice I also lowercased your function names. You should only capitalise your constructor functions.
Should I catch error here or inner scope where registerUser implemented?
You should catch errors where you can handle them - and you should handle errors at the end of the chain (by logging them, usually). If registerUser doesn't provide a fallback result, it doesn't need to handle anything, and it usually also doesn't need to log the error message on its own (if you want to, see here for an example).
See also Do I always need catch() at the end even if I use a reject callback in all then-ables?.
If you already are working with promises, then there's no need to create your own. When you call .then on a promise and provide a function saying what to do, that creates a new promise, which will resolve to whatever the function returns.
So your registerUser function should look something like this:
function registerUser(username, password) {
return getUser(username)
.then(user => {
if (user) {
throw new Error('User Exists');
}
return saveUser(username, password)
});
}
and you use it like this:
registerUser('starfox', 'doABarrelRoll')
.catch(error => console.log(error);
Note that if SaveUser causes an error, it will end up in this final .catch, since i didn't put any error handling inside registerUser. It's up to you to decide where you want to handle the errors. If it's something recoverable, maybe registerUser can handle it, and the outside world never needs to know. But you're already throwing an error if the user name exists, so the caller will need to be aware of errors anyway.
Additionally your getUser and saveUser functions might also be able to avoid creating their own promises, assuming the real implementation calls some function that returns a promise.
Your should use async/await syntax to avoid Promise Hell.
Change your code like this
/**
* This is a promise that resolve a user or reject if GetUser or SaveUser reject
*/
async function RegisterUser (username, password) {
var user = await GetUser(username)
if (user)
return user;
var userSaved = await SaveUser(username,password)
return userSaved
}
If you use RegisterUser inside a async function just code
async function foo () {
try {
var usr = await RegisterUser('carlos', 'secret123')
return usr
} catch (e) {
console.error('some error', e)
return null
}
}
Or if you use it like a promise
RegisterUser('carlos', 'secret123')
.then(usr => console.log('goood', usr))
.catch(e => console.error('some error', e))

Javascript - can't resolve this 'warning: promise was created in a handler'

Using sequelize.js in a nodejs app, and I have a promise.all that takes two promises (a user query, and a color query):
router.get(`/someEndPoint`, (req, res) => {
let userAccount = user.findOne({
where: {
id: //some ID
}
});
let colorStuff = color.findOne({
where: {
colorName: //some color
}
})
Promise.all([userAccount , colorStuff ]).then(([result1, result2]) => {
//do stuff, such as:
res.send('success');
}).catch(err => {
console.log(err)
});
});
At the part that says //do stuff, my console keeps giving me this warning:
a promise was created in a handler at... but was not returned from it,
see (URL that I can't post) at Function.Promise.attempt.Promise.try
I'm not sure how to resolve this. I thought after the .then that the promises are resolved?
Hard to tell without other context, but perhaps you need to return the Promise.all
return Promise.all([user, color])...
From the bluebird docs here: https://github.com/petkaantonov/bluebird/blob/master/docs/docs/warning-explanations.md#warning-a-promise-was-created-in-a-handler-but-was-not-returned-from-it
if there are any other promises created in the // do stuff area, be sure to return those as well.

$httpBackend doesn't seem to be flushing requests

I am testing my Angular app using ngDescribe. I don't think ngDescribe should be too much of a problem here, as it's just managing dependency injection for me. I first began to attempt my test the way the ngDescribe docs say, in the code below I have changed it to a more direct approach just to see if I could get any changes. I am calling a method that in turn calls $http.post('urlname', data); While debugging I can clearly see that my method gets all the way to where it calls post() but then it never continues. Because of this, my test always times out.
Hopefully I've just got something simple that's wrong! Here is my code. The test that fails is "Should work", all the others pass as expected.
Please also note that this is being processed by babel, both the test and the service, before being tested.
Here is the service, it works perfectly when being used. It has a few other variables involved that I have removed, but I assure you those variables are working correctly. While debugging for the tests, I can see that the await is hit, but it never continues past that, or returns. When used in the app, it returns exactly as expected. I THINK this has something to do with ngmock not returning as it should.
async function apiCall (endPoint, data) {
if (!endPoint) {
return false;
}
try {
return data ? await $http.post(`${endPoint}`, data) : await $http.get(`${endPoint}`);
} catch (error) {
return false;
}
}
Here are the tests:
ngDescribe({
name: 'Api Service, apiCall',
modules: 'api',
inject: ['apiService', '$httpBackend'],
tests (deps) {
let svc;
beforeEach(() => {
svc = deps.apiService;
});
it('is a function', () => {
expect(angular.isFunction(svc.apiCall)).toBe(true);
});
it('returns a promise', () => {
const apiCall = svc.apiCall();
expect(angular.isFunction(apiCall.then)).toBe(true);
});
it('requires an endpoint', async () => {
const apiCall = await svc.apiCall();
expect(apiCall).toBe(false);
});
it('should work', (done) => {
deps.http.expectPOST('fakeForShouldWork').respond({ success: true });
const apiCall = svc.apiCall('fakeForShouldWork', {});
apiCall.then(() => done()).catch(() => done());
deps.http.flush();
});
},
});
The method being called, apiCall, is simply a promise that is resolved by $http.post().then(); It will also resolve false if an error is thrown.
Since deps.http.expectPOST does not fail, I can tell that the outgoing request is sent. I validated this by changing it to expectGET and then I received an error about it being a POST.
I have tried moving the flush() method to all different parts of the test method, but it seems to make no difference.
Any thoughts? Thanks so much for your help!

Categories

Resources