Как продолжить выполнение последовательности Future, несмотря на неудачу?
traverse
метод из Future
объект останавливается при первом сбое. Я хочу толерантную / прощающую версию этого метода, которая при возникновении ошибок продолжается с остальной частью последовательности.
В настоящее время мы добавили следующий метод в наши утилиты:
def traverseFilteringErrors[A, B <: AnyRef]
(seq: Seq[A])
(f: A => Future[B]): Future[Seq[B]] = {
val sentinelValue = null.asInstanceOf[B]
val allResults = Future.traverse(seq) { x =>
f(x) recover { case _ => sentinelValue }
}
val successfulResults = allResults map { result =>
result.filterNot(_ == sentinelValue)
}
successfulResults
}
Есть лучший способ сделать это?
1 ответ
Действительно полезной вещью (вообще говоря) было бы возможность превратить ошибку будущего в правильную ценность. Или, другими словами, преобразовать Future[T]
в Future[Try[T]]
(успешное возвращаемое значение становится Success[T]
в то время как случай отказа становится Failure[T]
). Вот как мы можем это реализовать:
// Can also be done more concisely (but less efficiently) as:
// f.map(Success(_)).recover{ case t: Throwable => Failure( t ) }
// NOTE: you might also want to move this into an enrichment class
def mapValue[T]( f: Future[T] ): Future[Try[T]] = {
val prom = Promise[Try[T]]()
f onComplete prom.success
prom.future
}
Теперь, если вы делаете следующее:
Future.traverse(seq)( f andThen mapValue )
Вы получите успешный Future[Seq[Try[A]]]
, чье конечное значение содержит Success
экземпляр для каждого успешного будущего, и Failure
экземпляр для каждого неудачного будущего. При необходимости вы можете использовать collect
на этом последующем, чтобы бросить Failure
экземпляры и сохраняют только удачные значения.
Другими словами, вы можете переписать ваш вспомогательный метод следующим образом:
def traverseFilteringErrors[A, B](seq: Seq[A])(f: A => Future[B]): Future[Seq[B]] = {
Future.traverse( seq )( f andThen mapValue ) map ( _ collect{ case Success( x ) => x } )
}