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

  1. Call getUserByUsername and if rejects then call getUserByEmail
  2. Return the value of whoever resolves from the two promises above OR if both promises fail reject the whole chain.
  3. 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 and return 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.

许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top