Как вывести ошибку из оператора карты RxJS (угловой)

93

Я хочу, чтобы оператор карты моего наблюдаемого объекта выдавал ошибку в зависимости от условия. Например, если правильные данные API не получены. См. Следующий код:

private userAuthenticate( email: string, password: string ) {
    return this.httpPost(`${this.baseApiUrl}/auth?format=json&provider=login`, {userName: email, password: password})
        .map( res => { 
            if ( res.bearerToken ) {
                return this.saveJwt(res.bearerToken); 
            } else {
                // THIS DOESN'T THROW ERROR --------------------
                return Observable.throw('Valid token not returned');
            }
        })
        .catch( err => Observable.throw(this.logError(err) )
        .finally( () => console.log("Authentication done.") );
}

В основном, как вы можете видеть в коде, если ответ (объект res) не имеет bearerToken, я хочу выдать ошибку. Так что в моей подписке он входит во второй параметр (handleError), упомянутый ниже.

.subscribe(success, handleError)

Какие-либо предложения?

Хасан
источник
4
О чем throw 'Valid token not returned';?
Günter Zöchbauer
Не удается скомпилировать
Хассан
Пожалуйста, точное сообщение об ошибке.
Günter Zöchbauer
2
К сожалению, это не работает, return throw 'message here'но работает без returnключевого слова. Позвольте мне проверить, правильно ли он работает логически.
Хасан
Текст ошибки не получен в subscribeметоде, и .finally()в потоке также срабатывает триггер. (Однако казнь остановлена, и это хорошо)
Хассан

Ответы:

141

Просто выкинь ошибку внутри map()оператора. Все обратные вызовы в RxJS обернуты блоками try-catch, поэтому они будут перехвачены, а затем отправлены как errorуведомление.

Это означает, что вы ничего не возвращаете, а просто выдаете ошибку:

map(res => { 
  if (res.bearerToken) {
    return this.saveJwt(res.bearerToken); 
  } else {
    throw new Error('Valid token not returned');
  }
})

throwError()(Бывший Observable.throw()в RxJS 5) является Observable , что просто посылает errorуведомление , но map()не все равно , что вы вернетесь. Даже если вы вернете Observable, map()он будет передан как nextуведомление.

Последнее, что вам, вероятно, не нужно использовать .catchError()(ранее catch()в RxJS 5). Если вам нужно выполнить какие-либо побочные эффекты при возникновении ошибки, лучше использовать, например, tap(null, err => console.log(err))(бывший do()в RxJS 5).

Январь 2019: Обновлено для RxJS 6

Мартин
источник
1
Спасибо @martin - Да, ваше решение работает. У меня действительно была проблема внутри моего метода logError, на который указал @ GünterZöchbauer. Пришлось returnоттуда достать объект ошибки и теперь он отлично работает :) Спасибо!
Хасан
@martin: Не могли бы вы объяснить, почему мы не хотели бы, чтобы вы здесь .catch ()?
Боб
1
@Bob Потому что OP использовался catch()только для регистрации и повторной выдачи ошибки, что не нужно, если вы просто хотите выполнить побочный эффект (регистрацию ошибки), и его проще использовать, простоdo()
мартин
1
Это идентично return throwError(new Error('Valid token not returned'));?
Simon_Weaver 09
@Simon_Weaver нет, это не так. return throwError()возвращает Observable<never>, это просто немедленно прерывает наблюдаемый поток, не возвращаясь вообще.
Восстановить Монику
25

Если вам кажется, что это throw new Error()кажется ненаблюдаемым, вы можете использовать throwError(...)with switchMapвместо map(разница в том, что switchMapвозвращает новое наблюдаемое):

// this is the import needed for throwError()
import { throwError } from 'rxjs';


// RxJS 6+ syntax
this.httpPost.pipe(switchMap(res => { 
   if (res.bearerToken) {
      return of(this.saveJwt(res.bearerToken)); 
   } 
   else {
      return throwError('Valid token not returned');  // this is 
   }
});

или более кратко:

this.httpPost.pipe(switchMap(res => (res.bearerToken) ? 
                                    of(this.saveJwt(res.bearerToken)) : 
                                    throwError('Valid token not returned')
));

Поведение будет таким же, просто другой синтаксис.

Вы буквально говорите «переключиться» с наблюдаемого http в конвейере на другое наблюдаемое, которое либо просто «обертывает» выходное значение, либо обнаруживает новую наблюдаемую «ошибку».

Не забудьте указать, ofиначе вы получите запутанные сообщения об ошибках.

Кроме того, прелесть switchMap заключается в том, что вы можете вернуть целую новую «цепочку» команд, если хотите - для любой логики, с которой нужно работать saveJwt.

Simon_Weaver
источник
4
Как только я начал думать switchMapоб асинхронном ifутверждении - все стало намного
понятнее
3

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

Я бы решил, что будет возвращено отдельно от отображения, и наоборот. Я не уверен, какой оператор лучше всего подходит для этого, поэтому буду использовать tap.

this.httpPost.pipe(
  tap(res => { 
    if (!res.bearerToken) {
      throw new Error('Valid token not returned');
    }
  }),
  map(res => this.saveJwt(res.bearerToken)),
);
christo8989
источник
возвращаемое значение tapигнорируется. этот код делает не то, что написано
SF
Я все еще привыкаю к ​​rxjs. Было бы лучше использовать switchMap? Может кто-нибудь предложить другого оператора или отредактировать напрямую?
christo8989
Я думаю, что throw new Error()это лучший вариант на данный момент
SF