#scala #future #optional #for-comprehension
#scala #будущее #тип опции #для понимания
Вопрос:
у меня есть следующее для понимания. Предполагается удалить строку в моей базе данных, но только если строка существует (так что, если есть новости для данного идентификатора):
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)
}
Но я не знаю, как справиться со случаем, когда для данного идентификатора нет элемента. В настоящее время генерируется это исключение:
Unexpected exception[NoSuchElementException: Future.filter predicate is not satisfied]
Я надеюсь, что мой подход не ужасен: D
Я относительно новичок в scala.
Комментарии:
1. что означает возвращаемый тип
Int
?2. Строки, на которые влияет запрос в БД
3. Должен возвращаться
Future[Into]
и составлять оттуда, а не ждать
Ответ №1:
Использование Await
не такая уж отличная идея: лучше всего отложить блокировку как можно дольше.
ИМО, ни один элемент для данного идентификатора не должен быть ошибкой. newsDao.get
должен возвращать успешное будущее None
, newsDao.remove
если с этим идентификатором ничего нет, вы не должны вызывать deleteNews
идентификатор, который не существует, если вы можете с этим помочь, и общий результат должен быть просто успешно удален нулевыми строками (поскольку я бы посмотрел на контракт 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
возвращать голое Int
значение, вы можете Await.result
согласиться с тем, что иногда у вас будут возникать исключения и что это, вероятно, неоптимально.
Комментарии:
1. И да, я признаюсь, я не уверен, что смогу перевести это обратно в для понимания с первой попытки.
Ответ №2:
Как упоминал Леви, всегда старайтесь избегать блокировки, и когда вы сопоставляете шаблон, убедитесь, что вы обрабатываете все случаи.
Вы можете сделать это с помощью 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]
. Мне было бы интересно посмотреть, что говорят другие!