Обработка исключений в реактивных расширениях без остановки последовательности
Почему RX имеет следующую грамматику OnNext* (OnError|OnCompleted)
? вместо (OnNext|OnError)* OnCompleted
? Это довольно ясно с точки зрения реализации (также это имеет общую семантику с IEnumerable
а также yield
) но я думаю, что отличается от реальной жизни. В реальной жизни - производители генерируют смешанный поток данных и исключений (и исключения не нарушают производителя).
Вопрос: если я правильно понял, единственно возможное решение - сделать наблюдаемую возвращаемую сложную структуру данных объединенной из исходных данных и произведенных исключений (Observable.Timestamp()
а также .TimeInterval()
имеет похожую концепцию) или есть другие варианты?
На данный момент я пришел к следующему решению: Внутри наблюдаемого производителя я вручную обрабатываю исключения и просто передаю их следующей структуре данных, которая является результатом моей наблюдаемой
public class ValueOrException<T>
{
private readonly Exception ex;
private readonly T value;
public ValueOrException(T value, Exception ex)
{
this.value = value;
this.ex = ex;
}
public ValueOrException(T value)
{
this.value = value;
}
public T Value
{
get { return this.value; }
}
public Exception Ex
{
get { return this.ex; }
}
}
3 ответа
Если потребитель тот, кто знает, является ли Exception
ожидается или нет, тогда я говорю, что выбрасывать исключения здесь неправильно.
По сути, вы пытаетесь передать состояние или логику, а исключения не предназначены. Исключения предназначены для того, чтобы привести систему (или, по крайней мере, текущую операцию) к полной остановке, потому что произошло что-то, от чего код по сути не знает, как восстановиться.
В этом случае просто так получается, что Exception
является частью вашей бизнес-логики / состояния, но это не значит, что вы можете их выбросить. Вам необходимо передать их потребителю и затем обработать их.
Tuple<T, Exception>
будет работать в крайнем случае, но отсутствие специфичности вокруг типа (а также свойств), как правило, беспорядочно, поэтому я бы рекомендовал создать выделенный тип для передачи результатов вашей работы и раскрывается через IObservable<T>
,
OnError - это сообщение о том, что произошла неисправимая ошибка, поэтому поток должен быть сброшен. После восстановления следующие события не имеют смысла в контексте событий, произошедших до исключения.
Например, рассмотрим поток событий:
- Пользователь присоединился к чату
- Пользователь присоединился к чату
- !! Сбой чата, так что всех пользователей выгнали
- Пользователь присоединился к чату
Теперь потребитель потока использует функцию Aggregate для отображения количества чатов. Что должно отображаться в конце? Конечно, 1, а не 3. Мы должны начать считать с нуля после исключения.
ValueOrException выглядит как Либо тип из функционального программирования. Вы только что поменялись местами влево и вправо. По соглашению, Right используется для успеха, а Left - для неудачи.