Как правильно обрабатывать отдельные исключения в Future.sequence? - PullRequest
4 голосов
/ 02 июля 2019

Предположим, у меня есть приложение, которое загружает файлы с сервера и загружает их на другой сервер.

Загружаемые файлы не обязательно существуют на сервере.

Итак, когда файл не существует, я хочу пропустить его, не переходить к его загрузке и убедиться, что загружены / загружены только существующие файлы. (т.е. ни одно исключение при загрузке не должно останавливать все остальные процессы загрузки и выгрузки)

Будущее должно быть либо успешным, без результатов (в единицах), либо с исключением с ошибочным путем.

Какой стандартный способ справиться с этой ситуацией?

def downloadFile(path: String): Future[DownloadFile]
def uploadFile(file: DownloadFile): Future[Unit]


Future.sequence(
  paths.map { path =>
    for {
      downloadedFile <- downloadFile(path)
      _ <- uploadFile(downloadedFile)
    } yield Unit
  }
)

Ответы [ 2 ]

4 голосов
/ 02 июля 2019

Рассмотрим

  val listOfFutures = List(
    Future(1),
    Future(throw new RuntimeException("path/foo")),
    Future(2),
    Future(throw new RuntimeException("path/bar")),
  )

  Future.traverse(listOfFutures)(_.transform {
    case Success(v) => Try(Some(v))
    case Failure(e) => Try(None)
  }).map(_.flatten) andThen { case v => println(v) }

который выводит

Success(List(1, 2))

Примечание Future.sequence - это более простая версия Future.traverse.


Применяя комментарий, рассмотрите возможность инвертирования сплющивания следующим образом

Future.traverse(listOfFutures)(_.transform {
    case Success(v) => Try(None)
    case Failure(e) => Try(Some(e.getMessage))
  }).map { results =>
    if (results.flatten.nonEmpty) throw new RuntimeException(s"Bad paths: ${results.flatten.mkString(",")}")
    else ()
  } andThen { case v => println(v) }

который выводит

Failure(java.lang.RuntimeException: Bad paths: path/foo,path/bar)
1 голос
/ 02 июля 2019

Вы можете использовать Either

Future.sequence(
    Seq("path1", "path2").map { path =>
      (for {
        downloadedFile <- downloadFile(path)
        _ <- uploadFile(downloadedFile)
      } yield Right(Unit))
        .recover { case ex: Exception => Left(ex) }
    }
  )

Возвращает List(Right(object scala.Unit), Left(java.lang.Exception: Bad path))

или то же самое с Option:

Future.sequence(
    Seq("path1", "path2").map { path =>
      (for {
        downloadedFile <- downloadFile(path)
        _ <- uploadFile(downloadedFile)
      } yield None)
        .recover { case ex: Exception => Some(ex) }
    }
  )

Возвращает List(None, Some(java.lang.Exception: Bad path))

Список может быть отфильтрован.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...