如何在理解中处理失败的未来
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]
。我很想看看别人怎么说!
我有以下理解。它应该删除我的数据库中的一行,但前提是该行存在(所以如果有给定 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]
。我很想看看别人怎么说!