如何正确处理Future.sequence中的个别异常?

时间:2019-07-02 10:21:52

标签: scala

假设我有一个应用程序,可以从服务器下载文件并将其上传到另一台服务器。

下载文件不一定在服务器上。

因此,当文件不存在时,我想跳过它,而不是继续上传它,并确保仅下载/上传现有文件。 (即,任何单个下载异常都不应停止所有其他下载和上传过程)

未来应该成功,没有结果(单位),或者失败,并带有失败的路径。

处理这种情况的标准方法是什么?

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 个答案:

答案 0 :(得分:4)

考虑

  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.sequenceFuture.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 :(得分:1)

您可以使用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))

然后可以过滤列表。