Як викинути помилку з оператора карт 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

@Simon_Weaver ні, це не так. return throwError()повертає an Observable<never>, це просто перериває спостережуваний потік негайно, взагалі не повертаючись.
Відновити Моніку

25

Якщо ви відчуваєте, що throw new Error()здається незрозумілим, як ви можете використовувати throwError(...)з 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.


4
Одного разу я почав думати про це switchMapяк про асинхронне ifтвердження - речі мали набагато більше сенсу :-)
Simon_Weaver

3

Незважаючи на те, що на це питання вже дано відповіді, я хотів би поділитися своїм власним підходом (хоча він лише трохи відрізняється від вище).

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

this.httpPost.pipe(
  tap(res => { 
    if (!res.bearerToken) {
      throw new Error('Valid token not returned');
    }
  }),
  map(res => this.saveJwt(res.bearerToken)),
);

повернене значення tapігнорується. цей код робить інше, ніж сказано
sf

Я все ще звикаю до rxjs. Чи було б краще використовувати switchMap? Чи може хтось запропонувати іншого оператора або відредагувати безпосередньо?
christo8989

Я думаю, що запропонований throw new Error()на сьогодні найкращий варіант
sf
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.