es.davy.ai

Preguntas y respuestas de programación confiables

¿Tienes una pregunta?

Si tienes alguna pregunta, puedes hacerla a continuación o ingresar lo que estás buscando.

Esperar una secuencia de futuros con tiempo de espera sin fallar en la excepción de tiempo de espera.

Tengo una secuencia de Futures de Scala del mismo tipo.

Después de un tiempo limitado, quiero obtener un resultado para toda la secuencia mientras algunos futures pueden haber tenido éxito, algunos pueden haber fallado y algunos aún no se hayan completado, los futures no completados deben considerarse fallidos.

No quiero usar Await en cada futuro de manera secuencial.

Miré esta pregunta: https://stackoverflow.com/questions/29344430/scala-waiting-for-sequence-of-futures y traté de usar la solución de allí, es decir:

  private def lift[T](futures: Seq[Future[T]])(implicit ex: ExecutionContext) =
    futures.map(<em>.map { Success(</em>) }.recover { case t => Failure(t) })

def waitAll[T](futures: Seq[Future[T]])(implicit ex: ExecutionContext) =
    Future.sequence(lift(futures))

futures: Seq[Future[MyObject]] = ...
  val segments = Await.result(waitAll(futures), waitTimeoutMillis millis)

Pero aún obtengo una TimeoutException, supongo que porque algunos de los futures aún no se han completado.

Esa respuesta también afirma:

Ahora Future.sequence (elevado) se completará cuando cada futuro esté completo y representará éxitos y fallas usando Try.

Pero quiero que mi futuro se complete después de que haya pasado el tiempo de espera, no cuando cada futuro de la secuencia se haya completado. ¿Qué más puedo hacer?

Tags:  ,

Answer

  1. Avatar for davy.ai

    Como experto en TI especializado en Scala, puedes usar el método Future.firstCompletedOf en conjunto con Future.sequence y Future.failed para lograr tu objetivo. Aquí te presentamos una manera en la que podrías implementarlo:

    import scala.concurrent.ExecutionContext.Implicits.global
    import scala.concurrent.{Future, Promise}
    import scala.concurrent.duration._
    
    def waitAll[T](futures: Seq[Future[T]], timeout: Duration): Future[Seq[T]] = {
      val promise = Promise[Seq[T]]()
    
      // Crea un temporizador que completará la promesa con un fallo si se supera el tiempo de espera
      val timer = global.scheduleOnce(timeout) {
        promise.failure(new TimeoutException)
      }
    
      // Mapea cada Futuro a un Try, para distinguir entre éxitos y fallos
      val lifted = futures.map(_.map(Success(_)).recover { case t => Failure(t) })
    
      // Combina los Futures levantados en un solo Futuro que fallará rápidamente en caso de un primer fallo
      val combined = Future.firstCompletedOf(lifted)
    
      // Cuando el Futuro combinado se complete, cancela el temporizador y completa la promesa con una secuencia de éxitos o un fallo
      combined.foreach { result =>
        timer.cancel()
        val successes = result.collect { case Success(value) => value }
        if (successes.size == futures.size) {
          promise.success(successes)
        } else {
          promise.failure(new RuntimeException("No todos los Futures se completaron con éxito"))
        }
      }
    
      // Si alguno de los Futuros falla, cancela el temporizador y completa la promesa con un fallo inmediatamente
      Future.sequence(lifted.map(_.failed)).foreach { failures =>
        timer.cancel()
        promise.failure(failures.head)
      }
    
      promise.future
    }
    

    Puedes usar este método de la siguiente manera:

    val futures: Seq[Future[MyObject]] = ...
    val timeout: Duration = 10.seconds
    val segmentsFuture: Future[Seq[MyObject]] = waitAll(futures, timeout)
    
    // Ahora puedes usar segmentsFuture como cualquier otro Futuro, por ejemplo:
    segmentsFuture.onComplete {
      case Success(segments) => // Maneja el caso de éxito
      case Failure(t) => // Maneja el caso de fallo
    }
    

    Esta implementación utiliza una Promesa para crear un nuevo Futuro que tendrá éxito con una secuencia de resultados exitosos, o fallará con una TimeoutException o una RuntimeException que indique que no todos los Futuros se completaron con éxito. Iniciamos un temporizador que completará la Promesa con un fallo si se supera el tiempo de espera, y usamos Future.firstCompletedOf para crear un nuevo Futuro que se completará tan pronto como cualquier Futuro de la secuencia se complete, ya sea con éxito o con fallo. Cuando eso sucede, cancelamos el temporizador e inspeccionamos los resultados para determinar si todos los Futuros se completaron con éxito. Si lo hicieron, completamos la Promesa con una secuencia de resultados exitosos. De lo contrario, completamos la Promesa con un fallo que indique que no todos los Futuros se completaron con éxito. Si alguna de los Futuros falla antes de que se acabe el tiempo de espera, cancelamos el temporizador y completamos la Promesa de inmediato con el fallo del primer Futuro fallido.

Comments are closed.