How to get the value of the first promise that succeeds in a chain of promises
-
21-12-2019 - |
سؤال
I'm looking for a way to return a promise only if another promise fails.
How can I do that?
The promises that I have look like this ( of course I've remove non-related code ) :
getUserByUsername = function(username) {
return promise(function(resolve, reject) {
user = userModel.getByUsername(username);
if ( user ) { resolve(user); } else { reject("user not found"); }
});
};
getUserByEmail = function(email) {
return promise(function(resolve, reject) {
user = userModel.getByEmail(email);
if ( user ) { resolve(user); } else { reject("user not found"); }
});
};
checkPassword = function(user, password) {
return promise(function(resolve, reject) {
if ( user.isValidPassword(password) ) { resolve() } else { reject("password mismatch"); }
});
};
And I want to combine them in the following sequence
- Call
getUserByUsername
and if rejects then callgetUserByEmail
- Return the value of whoever resolves from the two promises above OR if both promises fail reject the whole chain.
- Call
checkPassword
with the value and if it rejects, reject the whole chain
So far I have done this and it seems to work. ( I'm using when.js btw )
getUserByUsername(usernameOrEmail)
.then(
function(user) { return user; },
function() { return getUserByEmail(usernameOrEmail) }
)
.then( function(user) { return checkPassword(user, password) } )
.done(
function() { console.log('success'); },
function(err) { console.log('failure', err) }
);
So this code works but I'm pretty sure there should be more elegant way of doing this ( I think my code looks more like a hack ).
المحلول
What about something like:
getUserByUsername(usernameOrEmail).
catch(getUserByEmail.bind(null, usernameOrEmail)).
then(function(user){
if(!isValidPassword(user, password)) throw new Error("Invalid Password");
}).then(function(){
// success
}, function(){
//fail
});
Key things:
- You can
throw
to reject promises andreturn
to continue chains. There is little use to the promise constructor. - You can omit the first parameter in
.then
by doing.then(null, function(){...
however it is even simpler to use.catch
which is an alias for that. More generally.then
ignores parameters that are not functions. - Rejections and return values propagate, if you don't handle a rejections through a
.catch
(or a second argument for.then
) it will simply move across the chain until you do. The same goes for return values - this is why the first two lines here encompass:- Try to get the user by username
- If that fails, try by email.
In general what you describe is very common with promises and parallels to exceptions in synchronous code very well. It's important to learn how to do this sort of thing effectively.
لا تنتمي إلى StackOverflow