Как получить значение первого промиса, который преуспевает в цепочке промисов

Я ищу способ вернуть обещание только в случае сбоя другого обещания.

Как я могу это сделать?

Обещания, которые у меня есть, выглядят так (конечно, я удалил несвязанный код):

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"); }
    });
};

И я хочу объединить их в следующей последовательности

  1. Позвоните getUserByUsername, а если откажет, позвоните getUserByEmail
  2. Верните значение того, кто разрешает два обещания выше, ИЛИ, если оба обещания не работают, отклоните всю цепочку.
  3. Вызовите checkPassword со значением и, если он отклонит, отклоните всю цепочку

До сих пор я сделал это, и это, кажется, работает. (кстати, я использую when.js)

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) }
);

Итак, этот код работает, но я уверен, что должен быть более элегантный способ сделать это (я думаю, что мой код больше похож на хак).


person drinchev    schedule 30.07.2014    source источник


Ответы (1)


Как насчет чего-то вроде:

 getUserByUsername(usernameOrEmail).
 catch(getUserByEmail.bind(null, usernameOrEmail)).
 then(function(user){
     if(!isValidPassword(user, password)) throw new Error("Invalid Password");
 }).then(function(){
     // success
 }, function(){
    //fail
 });

Ключевые вещи:

  • Вы можете throw отказаться от обещаний и return продолжить цепочки. От конструктора промисов мало пользы.
  • Вы можете опустить первый параметр в .then, выполнив .then(null, function(){..., однако еще проще использовать .catch, который является псевдонимом для этого. В более общем случае .then игнорирует параметры, не являющиеся функциями.
  • 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
    • Если это не поможет, попробуйте по электронной почте.

В целом то, что вы описываете, очень хорошо сочетается с обещаниями и параллелями с исключениями в синхронном коде. Важно научиться делать подобные вещи эффективно.

person Benjamin Gruenbaum    schedule 30.07.2014
comment
Чувак, ты слишком быстр. У меня нет возможности написать ответ, когда ты рядом :-) - person Bergi; 30.07.2014
comment
Большое спасибо за отличное разъяснение. Я думаю, что понял вашу точку зрения на примере, хотя я думаю, что вы пропустили один ) во второй строке. Вы имеете в виду .catch(getUserByEmail.bind(null,usernameOrEmail)).then... ? - person drinchev; 30.07.2014
comment
Да, да, я сделал. Хороший улов. - person Benjamin Gruenbaum; 30.07.2014