如何在理解中处理失败的未来

How to handle a failed future in a for-comprehension

我有以下理解。它应该删除我的数据库中的一行,但前提是该行存在(所以如果有给定 id 的新闻):

override def deleteNews(newsId: Long): Int = {
    val getAndDelete = for {
         Some(news) <- newsDao.get(newsId)// returns Future[Option[News]]
         delete <- newsDao.remove(news)   // returns Future[Int]
     } yield delete
     Await.result(getAndDelete, responseTimeout)
}

但是我不知道如何处理给定 id 没有元素的情况。当前抛出此异常:

Unexpected exception[NoSuchElementException: Future.filter predicate is not satisfied]

我希望我的方法不会太糟糕:D

我对 scala 比较陌生。

使用 Await 并不是什么好主意:最好尽可能延迟阻塞。

IMO,给定 ID 的任何元素都不应失败。 newsDao.get 应该 return None 的成功未来,如果那个 ID 没有任何东西,你不应该在一个不存在的 ID 上调用 newsDao.remove 如果你能帮助它,并且总体结果应该只是成功删除了零行(因为我将 deleteNews 的合同视为确保在调用和 return 之间的某个时刻没有与 newsId(当然,这里有一些关于数据竞争的问题……))。

因此,假设您无法更改 newsDao 的实现:

val getFut: Future[Option[News]] =
  newsDao.get(newsId).recover {
    // can still fail for other reasons
    case _: NoSuchElementException => None
  }

// I really prefer map/flatMap directly vs. for-comprehension sugar, especially when dealing with multiple monadicish things

// Not the most succinct, but leaving meaningful names in for documentation
val getAndRemove =
  getFut.flatMap { newsOpt =>
    newsOpt.map { news =>
      newsDao.remove(news)
    }.getOrElse(Future.successful(0))
  }

如果你仍然需要 deleteNews 到 return 一个简单的 Int,你可以 Await.result 并接受你有时会抛出异常,这可能是次优。

正如 Levi 提到的,始终尽量避免阻塞,并且在进行模式匹配时,确保处理所有情况。

您可以使用 for-comprehension 执行此操作,如下所示:

def deleteNews(newsId: Long): Future[Option[Int]] =
  for {
    news   <- newsDao.get(newsId)
    delete <- Future.sequence(news.map(id => newsDao.remove(id)).toList)
  } yield delete.headOption

老实说,我没有用这个技巧从 Option[Future]Future[Option]。我很想看看别人怎么说!