Понимание цепочки обещаний на этом простом примере - PullRequest
0 голосов
/ 07 мая 2018

Мне нужна следующая логика, но я не могу ее понять. Когда пользователь найден (не undefined), мне нужно сравнить пароль (еще одно обещание), который возвращает boolean). Надо:

  • возврат undefined (HTTP 404), когда пользователь не существует
  • throw ForbiddenError (HTTP 403), если пароль неправильный
  • возвращает user (HTTP 200), если пользователь существует и пароль совпадает

Первая попытка (некрасиво, нечитабельно):

  @Post()
  login(
    @BodyParam('username', { required: true }) username: string,
    @BodyParam('password', { required: true }) plainPassword: string,
  ) {

    return this.userRepository.findOne({ username: username, enable: true })
      .then ((user: User | undefined) => {
        if (!user) {
          return undefined; // 404
        }

        return bcrypt.compare(plainPassword, user.password)
          .then(passwordMatch => {
            if (!passwordMatch) {
              throw new ForbiddenError('Authentication failed.'); // 403
            }

            return user; // 200
          });
      });
  }

Вторая попытка не работает, всегда возвращает 'ok':

return this.userRepository.findOne({ username: username, enable: true })
  .then((user: User | undefined) => {
    if (!user) {
      return undefined; // 404
    }

    return bcrypt.compare(password, user.password);
  })
  .then(passwordMatch => {
    // Doesn't work: this is executed every time (even if user is undefined).

    return 'ok';
  });

Ответы [ 2 ]

0 голосов
/ 07 мая 2018

Когда вы return undefined, первое then() обещание обратного вызова разрешается до undefined.

Затем выполняется ваш второй then() обратный вызов, получая undefined в качестве параметра.

Вы должны изменить второй обратный вызов, чтобы проверить это.

0 голосов
/ 07 мая 2018

Ваш обработчик then в конце всегда запускается (хорошо, если обещания не отклоняются), потому что первое обещание разрешается с undefined, если пользователь не существует, или с логическим значением, если пользователь делает есть.

Ваша вложенная версия была в порядке. Если вам нужно вернуть user в успешном случае, возможно, это правильный путь.

Но если вам просто нужно вернуть 'ok', как в вашем втором примере кода в успешном случае, вы могли бы сгладить вещи, вам просто нужно обработать undefined, который вы получите, если есть нет пользователя Мы также можем воспользоваться тем фактом, что вы знаете, что user будет иметь значение undefined, если пользователь не был найден:

return this.userRepository.findOne({ username: username, enable: true })
  // The `then` below returns `undefined` if `user` is `undefined`, the promise from `compare` otherwise
  .then((user: User | undefined) => user && bcrypt.compare(password, user.password))
  .then(passwordMatch => {
    if (passwordMatch === undefined) {
      // No user
      return undefined;
    } else if (!passwordMatch) {
      // Bad password
      throw new ForbiddenError('Authentication failed.'); // 403
    } else {
      // All good
      return 'ok';
    }
  });

Если вы хотите сгладить его и вернуть user, то вам нужно передать user следующему обработчику:

return this.userRepository.findOne({ username: username, enable: true })
  .then((user: User | undefined) => {
    return !user
        ? {user} // will be {undefined}
        : bcrypt.compare(password, user.password)
            .then(passwordMatch => ({user, passwordMatch})); // *** Note making an object
  })
  .then(({user, passwordMatch}) => { // *** Note destructuring
    if (user === undefined) {
      // No user
      return undefined;
    } else if (!passwordMatch) {
      // Bad password
      throw new ForbiddenError('Authentication failed.'); // 403
    } else {
      // All good
      return user; // 200
    }
  });

(Этот первый обработчик then мог быть сжатой стрелкой, как в первом блоке кода выше, но он стал уродливым.)

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...