Я обнаружил, что построение большого количества фьючерсов для одного пользовательского запроса, как правило, является плохой практикой. Эти фьючерсы могут заполнить контекст выполнения, который повлияет на другие запросы. Это маловероятно, чего вы действительно хотите. Хранение фьючерсов число малое - создать новые фьючерсы только для понятий, используя flatMap и т. Д. Но иногда может потребоваться создать Будущее для каждого элемента Seq. Использование проблемы Future.sequence или Future.traverse, описанной выше. Так что я в конечном итоге с этим решением, которое не создает Фьючерс для каждого элемента коллекции одновременно:Есть ли сборка в «медленной» версии Future.traverse?
def ftraverse[A, B](xs: Seq[A])(f: A => Future[B])(implicit ec: ExecutionContext): Future[Seq[B]] = {
if(xs.isEmpty) Future successful Seq.empty[B]
else f(xs.head) flatMap { fh => ftraverse(xs.tail)(f) map (r => fh +: r) }
}
Интересно, может быть, я изобретать колесо и на самом деле такая функция уже существует где-то в библиотеке стандартной в Scala? Также я хотел бы знать, вы столкнулись с описанной проблемой и как вы ее разрешили? Может быть, если это хорошо известная проблема с Futures, я должен создать запрос на перенос в Future.scala, чтобы эта функция (или более обобщенная версия) была включена в стандартную библиотеку?
UPD: Более общая версия, ти ограниченного параллелизма:
def ftraverse[A, B](xs: Seq[A], chunkSize: Int, maxChunks: Int)(f: A => Future[B])(implicit ec: ExecutionContext): Future[Seq[B]] = {
val xss = xs.grouped(chunkSize).toList
val chunks = xss.take(maxChunks-1) :+ xss.drop(maxChunks-1).flatten
Future.sequence{ chunks.map(chunk => ftraverse(chunk)(f)) } map { _.flatten }
}
Посмотрите на «Задачу» в скалясе. Он реализует 'traverse', как определено в классе Haskell Traversable, который ведет себя таким образом (по одной операции за раз). –