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

Я хочу выбросить ошибку из моего наблюдаемого оператора карты, основываясь на условии. Например, если правильные данные 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 object) не имеет "bearerToken", я хочу выбросить ошибку. Так что в моей подписке он переходит во второй параметр (handleError), упомянутый ниже.

.subscribe(success, handleError)

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

Ответ 1

Просто скинуть ошибку внутри оператора 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

Ответ 2

Если вам кажется, что throw new Error() кажется ненаблюдаемым, вы можете использовать switchMap:

// 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.httpPost.pipe(switchMap(res => (res.bearerToken) ? 
                                    of(this.saveJwt(res.bearerToken)) : 
                                    throwError('Valid token not returned')
));

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

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

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

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

Ответ 3

Попробуй это

Observable.throw(new Error('error!'));