Лучший способ обработки ошибок с помощью Monix Observable

#scala #observable #monix

#scala #наблюдаемый #monix

Вопрос:

Я пытаюсь создать реактивное приложение с monix 3.0.0-RC1.

Например, у a есть последовательность значений Int, а второй элемент неверен. Я могу использовать Oservable.raiseError(...) для обработки этого:

   Observable.fromIterable(Seq(1,2,3))
    .flatMap( i =>
      if (i == 2) Observable.raiseError(new Exception("Wrong i"))
      else Observable.pure(i)
    )
    .doOnNext(i => println(s"Processing $i"))
    .foreachL(i => println(s"Finished $i"))
    .onErrorHandle(_ => Task.unit)
  

Мне не нравится генерируемое исключение в приведенном выше коде.

С другой стороны, я могу использовать Scala Either :

 Observable.fromIterable(Seq(1,2,3))
  .map( i =>
    if (i == 2) Left("Wrong i")
    else Right(i)
  )
  .doOnNext(either => either.map( i => println(s"Processing $i") ))
  .foreachL(either => either.map( i => println(s"Finished $i") ))
  

Но either => either.map(...) на каждом шаге не круто.

Какой лучший способ обработки ошибок?

Комментарии:

1. Исключение на самом деле не «выбрасывается». Он передается по каналу ошибок как часть дизайна Observable для ошибок. Почему вам это не нравится? Тот факт, что вы не хотите иметь дело с этим на каждом шаге, является намеком на то, что это должно быть сделано, как в первом примере.

2. Как сказал @AlvaroCarrasco, Exception никогда не выбрасывается, и ваш первый пример использует Observable s, поскольку они улучшаются для использования.

Ответ №1:

Вы можете использовать collect , если вас интересует только правильный результат, например

 val logError = {
  case Right(_) => Task.unit
  case Left(i) => Task.delay(println(s"An error occured $i"))
}

Observable.fromIterable(Seq(1,2,3))
  .map( i =>
    if (i == 2) Left("Wrong i")
    else Right(i)
  )
  .doOnNext(logError)
  .collect {
    case Right(value) => value
  }
  .foreachL(i => println(i))

  

Ответ №2:

Если вы хотите удалить плохой элемент, вы можете сделать это таким образом.

 Observable.fromIterable(1 to 3)
      .mapEval(p => { if(i == 2) Task.raiseError(new Exception) 
                      else Task.now(i)
                    }.attempt )
      .collect { case Right(v) => v }
      .foreachL(println)
      .runToFuture

/*
1
3
*/
  

В основном продолжайте использовать Task и attempt превращать композицию задачи в задачу [Либо].