RxJS: как выдать ошибку, а затем поймать ее?

Это связано не с ошибкой, с которой я сталкиваюсь, а с проблемой синтаксиса.

Рабочий процесс прост:

  • сделать HTTP-запрос, который возвращает логическое значение
  • если логическое значение истинно, то продолжить
  • если логическое значение равно false, запишите предупреждение и остановите поток.

Чтобы управлять этим, мой текущий код таков:

Шаблон

private _getBoolean() { return this.http.get(...); }
private _getData() { return this.http.get(...); }

Текущий код

public getData() {
  return this._getBoolean().pipe(
    filter(bool => {
      if(!bool) {
        console.warn('Wrong server answer, stream stopped');
        return false;
      }
      return true;
    }),
    switchMap(bool => this._getData())
  );
}

И я не знаю почему, но мне это не кажется естественным и оптимизированным.

Я думал, что будет что-то, упрощающее синтаксис, что-то вроде этого

public getData() {
  return this._getBoolean().pipe(
    throwError(bool => bool ? new Error('Wrong server answer, stream stopped') : null),
    catchError(err => console.warn(err)),
    switchMap(bool => this._getData())
  );
}

Есть ли что-то в этом роде, или у меня правильный синтаксис?


person Community    schedule 09.11.2018    source источник


Ответы (3)


Рассмотрим следующую наблюдаемую ниже, которая выдает значения от 1 до 4. Допустим, ошибка выдается, когда значение равно 3. Эта ошибка может быть обнаружена в операторе catchError или внутри subscribe. Я считаю, что это зависит от конкретного варианта использования, позволяете ли вы всплывать ошибке полностью до подписчика или ее следует обрабатывать где-то выше по течению от подписчика.

of(1, 2, 3, 4).pipe(
  // Throw error when value is 3
  tap(value => { if(value === 3) throw new Error('Oops!') }),
  catchError(err => {
    console.log('Catch error in operator', err);

    // You can rethrow the same error or a new error
    // return throwError(err);

    // Or you can handle the error and return a new observable
    return of(3)
  })
).subscribe(
  value => console.log(value),
  // If `catchError` returns a new observable, then the error 
  // function here will not be called
  err => console.log('Catch error within subscribe', err),
  () => console.log('Done!')
)

Обратите внимание, что в этом примере, даже если ошибка обрабатывается, наблюдаемая завершается, и значение 4 никогда не выдается. Если вы хотите, чтобы наблюдаемое оставалось активным при возникновении ошибки, посмотрите этот ответ StackOverflow.

person Sam Herrmann    schedule 09.11.2018
comment
Я тоже пробовал это решение, так как это первое, что я нашел в Интернете. Проблема с этим кодом заключается в том, что выданная ошибка является ошибкой Javascript и отображается как фактическая ошибка JS в консоли. Я могу показаться придирчивым, но в моем коде нет ошибки JS, просто с сервера возвращается логическое значение false... Так что это решение мне не подходит (но я проголосую за него, потому что оно может помочь другим) - person ; 09.11.2018
comment
Есть ли что-то, что мешает вам использовать tap(bool => { if(bool) throw new Error('Wrong server answer') }), т.е. вы создаете ошибку JS, когда сервер дает вам false? И пока вы его поймаете, он не должен отображаться в консоли. - person Sam Herrmann; 09.11.2018
comment
Вот в чем фокус, на самом деле вы его не поймаете, попробуйте, вы увидите, что он появится в вашей консоли. И это беспокоит меня (ну не меня, мой N+1), потому что ответ сервера - 200, а не 400 или 500, так что это не ошибка (я упростил пример, я, конечно, не запрашиваю простое логическое значение из сервер) - person ; 09.11.2018

вместо:

public getData() {
  return this._getBoolean().pipe(
    throwError(bool => bool ? new Error('Wrong server answer, stream stopped') : null),
    catchError(err => console.warn(err)),
    switchMap(bool => this._getData())
  );
}

почему бы не что-то вроде:

public getData() {
  return this._getBoolean().pipe(
    tap(result => !result && throwError('Wrong server answer, stream stopped')),
    switchMap(bool => this._getData()),
    catchError(err => console.warn(err))
  );
}
person dee zg    schedule 09.11.2018
comment
Будет ли это поймать ошибку, если вы ничего не вернете? (сейчас сижу на телефоне, проверить не могу) - person ; 09.11.2018
comment
что значит ничего не возвращать? отсюда? и какая ошибка в таком случае? - person dee zg; 09.11.2018

Я не уверен, правильно ли я понимаю вашу проблему, но вы можете заменить

    console.warn('Wrong server answer, stream stopped');
    return false;

С

   Observable.throw('Some error cause')

А затем перехватите его с помощью ближайшего блока catch в вашем потоке, что дает вам в основном изменение: - Остановить поток, если вы повторно выдаете ошибку - Перезапустите его, если вы вернете наблюдаемый ввод - верните полностью новый наблюдаемый

public getData() {
  return this._getBoolean().pipe(
    filter(bool => {
      if(!bool) {
        console.warn('Wrong server answer, stream stopped');
        //return false;
        Observable.throw('I got false where I expected true')
      }
      return true;
    }),
    switchMap(bool => this._getData())
  );
}

А потом:

getData()
.any()
.operator()
.you()
.wish()
.catch(e => {
  /* Here stream will be terminated on thrown error */
})
person Tomas    schedule 09.11.2018
comment
Разве использование наблюдаемого прототипа не считается плохой практикой в ​​rxjs 6? - person ; 09.11.2018
comment
В RxJS 6 да, это так. К сожалению, я основываю свой опыт на 5.5 :( Вы должны получить тот же результат, обернув все операторы, кроме catch, трубой - person Tomas; 09.11.2018
comment
Ну то же самое, я думаю, что ловить - это плохая практика, вы должны использовать второй обратный вызов подписки ... Отсюда мой вопрос, как и вы, я основывал свой опыт на ‹ 6, и я еще не понял, как с этим справиться ... - person ; 09.11.2018
comment
@trichetriche, не могли бы вы поделиться ссылкой на то, что поймать в наблюдаемой цепочке - плохая практика? Второй обратный вызов в подписке имеет другую цель: он будет обрабатывать ошибки в подписках, но в этот момент этот наблюдаемый объект исчезнет. так что это другое использование. - person dee zg; 09.11.2018
comment
Кстати, Observable.throw стал оператором throwError() - person dee zg; 09.11.2018
comment
@deezg см. Почему? в pipeable операторы : то, что касается прототипа, не рекомендуется (по крайней мере, так я понял) - person ; 09.11.2018
comment
@trichetriche Я понимаю это, но не знаю, как catchError и throwError касаются прототипа? - person dee zg; 09.11.2018
comment
Я говорил о catch, а не catchError :) - person ; 09.11.2018
comment
@trichetriche о, хорошо, тогда я ошибся. я думал, вы говорите, что не ловите (каким бы ни был синтаксис) в цепочке, а только во втором обратном вызове subscribe :) - person dee zg; 09.11.2018