Я обнаружил, что создание большого количества фьючерсов для одного запроса пользователя, как правило, является плохой практикой. Эти фьючерсы могут заполнить контекст выполнения, который повлияет на другие запросы. Это маловероятно, чего вы действительно хотите. Хранение фьючерсов число малое - создать новые фьючерсы только для понятий, используя flatMap и т.д. Но иногда может потребоваться создать Будущее для каждого элемента Seq. Использование проблемы Future.sequence или 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: более общая версия с ограниченным parallelism:
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 }
}