Buscar..


Creando un futuro

import scala.concurrent.Future
import scala.concurrent.ExecutionContext.Implicits.global

object FutureDivider {
    def divide(a: Int, b: Int): Future[Int] = Future {
        // Note that this is integer division.
        a / b
    }
}

Simplemente, el método de divide crea un Futuro que se resolverá con el cociente de a sobre b .

Consumiendo un futuro exitoso

La forma más fácil de consumir un futuro exitoso, o más bien, obtener el valor dentro del futuro, es usar el método de map . Supongamos que un cierto código llama a la divide método de la FutureDivider objeto del ejemplo "Creando un futuro". ¿Cómo debería ser el código para obtener el cociente de a sobre b ?

object Calculator {
    def calculateAndReport(a: Int, b: Int) = {
        val eventualQuotient = FutureDivider divide(a, b)
        
        eventualQuotient map {
            quotient => println(quotient)
        }
    }
}

Consumiendo un futuro fallido

A veces, el cálculo en un futuro puede crear una excepción, lo que hará que el futuro falle. En el ejemplo de "Creación de un futuro", ¿qué sucede si el código de llamada pasó 55 y 0 al método de divide ? Lanzaría una ArithmeticException después de tratar de dividir por cero, por supuesto. ¿Cómo se manejaría eso en consumir código? En realidad, hay un puñado de maneras de lidiar con los fracasos.

Manejar la excepción con recover y coincidencia de patrones.

object Calculator {
    def calculateAndReport(a: Int, b: Int) = {
        val eventualQuotient = FutureDivider divide(a, b)
    
        eventualQuotient recover {
            case ex: ArithmeticException => println(s"It failed with: ${ex.getMessage}")
        }
    }
}

Maneje la excepción con la proyección failed , donde la excepción se convierte en el valor del futuro:

object Calculator {
    def calculateAndReport(a: Int, b: Int) = {
        val eventualQuotient = FutureDivider divide(a, b)
    
        // Note the use of the dot operator to get the failed projection and map it.
        eventualQuotient.failed.map {
            ex => println(s"It failed with: ${ex.getMessage}")
        }
    }
}

Poniendo el futuro juntos

Los ejemplos anteriores demostraron las características individuales de un futuro, manejando casos de éxito y fracaso. Generalmente, sin embargo, ambas características se manejan mucho más tersamente. Aquí está el ejemplo, escrito de una manera más ordenada y más realista:

object Calculator {
    def calculateAndReport(a: Int, b: Int) = {
        val eventualQuotient = FutureDivider divide(a, b)
        
        eventualQuotient map {
            quotient => println(s"Quotient: $quotient")
        } recover {
            case ex: ArithmeticException => println(s"It failed with: ${ex.getMessage}")
        }
    }
}

Secuenciación y travesía de futuros.

En algunos casos es necesario calcular una cantidad variable de valores en futuros separados. Suponga que tiene una List[Future[Int]] , pero en su lugar debe procesarse una List[Int] . Entonces la pregunta es cómo convertir esta instancia de la List[Future[Int]] en un Future[List[Int]] . Para este propósito existe el método de sequence en el objeto compañero Future .

def listOfFuture: List[Future[Int]] = List(1,2,3).map(Future(_))
def futureOfList: Future[List[Int]] = Future.sequence(listOfFuture)

En general, la sequence es un operador comúnmente conocido dentro del mundo de la programación funcional que transforma F[G[T]] en G[F[T]] con restricciones a F y G

Hay un operador alternativo llamado traverse , que funciona de manera similar pero toma una función como un argumento adicional. Con la función de identidad x => x como parámetro, se comporta como el operador de sequence .

def listOfFuture: List[Future[Int]] = List(1,2,3).map(Future(_))
def futureOfList: Future[List[Int]] = Future.traverse(listOfFuture)(x => x)

Sin embargo, el argumento adicional permite modificar cada instancia futura dentro del listOfFuture dado. Además, el primer argumento no necesita ser una lista de Future . Por lo tanto es posible transformar el ejemplo de la siguiente manera:

def futureOfList: Future[List[Int]] = Future.traverse(List(1,2,3))(Future(_))

En este caso, la List(1,2,3) se pasa directamente como primer argumento y la función de identidad x => x se reemplaza con la función Future(_) para envolver de manera similar cada valor de Int en un Future . Una ventaja de esto es que la List[Future[Int]] intermedia List[Future[Int]] puede omitirse para mejorar el rendimiento.

Combina Futuros Múltiples - Para Comprensión

La comprensión es una forma compacta de ejecutar un bloque de código que depende del resultado exitoso de múltiples futuros.

Con f1, f2, f3 tres Future[String] que contendrán las cadenas one, two, three respectivamente,

val fCombined = 
    for {
        s1 <- f1
        s2 <- f2
        s3 <- f3
    } yield (s"$s1 - $s2 - $s3")

fCombined será un Future[String] contiene la cadena one - two - three una vez que todos los futuros se hayan completado con éxito.

Tenga en cuenta que aquí se supone un ExectionContext implícito.

Además, tenga en cuenta que para la comprensión es solo un azúcar sintáctico para un método flatMap, por lo que la construcción de objetos Futuros dentro del cuerpo eliminaría la ejecución concurrente de bloques de código incluidos en futuros y llevaría a un código secuencial. Lo ves en el ejemplo:

val result1 = for {
  first <- Future {
    Thread.sleep(2000)
    System.currentTimeMillis()
  }
  second <- Future {
    Thread.sleep(1000)
    System.currentTimeMillis()
  }
} yield first - second

val fut1 = Future {
  Thread.sleep(2000)
  System.currentTimeMillis()
}
val fut2 = Future {
  Thread.sleep(1000)
  System.currentTimeMillis()
}
val result2 = for {
  first <- fut1
  second <- fut2
} yield first - second

Valor encerrada por result1 objeto sería siempre negativo, mientras que result2 serían positivos.

Para obtener más detalles sobre la comprensión y el yield en general, consulte http://docs.scala-lang.org/tutorials/FAQ/yield.html



Modified text is an extract of the original Stack Overflow Documentation
Licenciado bajo CC BY-SA 3.0
No afiliado a Stack Overflow