Ricerca…


Creare 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
    }
}

Molto semplicemente, il metodo di divide crea un futuro che si risolverà con il quoziente di a b .

Consumare un futuro di successo

Il modo più semplice per consumare un futuro di successo - o meglio, ottenere il valore all'interno del futuro - è utilizzare il metodo della map . Supponiamo che alcuni codici chiamino il metodo di divide dell'oggetto FutureDivider dall'esempio "Creating a Future". Come dovrebbe apparire il codice per ottenere il quoziente di a b ?

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

Consumare un futuro fallito

A volte il calcolo in un futuro può creare un'eccezione, che farà fallire il futuro. Nell'esempio "Creating a Future", cosa accadrebbe se il codice chiamante passasse 55 e 0 al metodo divide ? Avrebbe gettato un ArithmeticException dopo aver provato a dividere per zero, ovviamente. Come sarebbe gestito nel codice che consuma? Ci sono in realtà una manciata di modi per affrontare i fallimenti.

Gestire l'eccezione con il recover e la corrispondenza del modello.

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}")
        }
    }
}

Gestire l'eccezione con il failed proiezione, dove l'eccezione diventa il valore 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}")
        }
    }
}

Mettere insieme il futuro

Gli esempi precedenti hanno dimostrato le caratteristiche individuali di un futuro, gestendo casi di successo e fallimento. Di solito, tuttavia, entrambe le funzionalità vengono gestite in modo molto più preciso. Ecco l'esempio, scritto in modo più ordinato e più realistico:

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}")
        }
    }
}

Sequencing and traversing Futures

In alcuni casi è necessario calcolare una quantità variabile di valori su Futures separati. Si supponga di avere una List[Future[Int]] , ma invece una List[Int] deve essere elaborata. Quindi la domanda è come trasformare questa istanza di List[Future[Int]] in un Future[List[Int]] . Per questo scopo c'è il metodo di sequence sull'oggetto compagno Future .

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

In generale, la sequence è un operatore comunemente noto nel mondo della programmazione funzionale che trasforma F[G[T]] in G[F[T]] con restrizioni a F e G

Esiste un operatore alternativo chiamato traverse , che funziona in modo simile ma prende una funzione come argomento aggiuntivo. Con la funzione di identità x => x come parametro si comporta come l'operatore di sequence .

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

Tuttavia, l'argomento extra consente di modificare ogni istanza futura all'interno della listOfFuture . Inoltre, il primo argomento non deve essere una lista di Future . Pertanto è possibile trasformare l'esempio come segue:

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

In questo caso l' List(1,2,3) viene passato direttamente come primo argomento e la funzione di identità x => x viene sostituita con la funzione Future(_) per avvolgere in modo simile ciascun valore Int in un Future . Un vantaggio di questo è che l' List[Future[Int]] intermedio List[Future[Int]] può essere omesso per migliorare le prestazioni.

Combina più Futures - Per Comprensione

La comprensione è un modo compatto per eseguire un blocco di codice che dipende dal risultato positivo di più futuri.

Con f1, f2, f3 tre Future[String] che conterranno le stringhe one, two, three rispettivamente,

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

fCombined sarà una Future[String] contenente la stringa one - two - three una volta che tutti i futures sono stati completati con successo.

Si noti che qui viene assunto un implicito ExectionContext.

Inoltre, tieni a mente che per la comprensione è solo uno zucchero sintattico per un metodo flatMap, quindi la costruzione di oggetti futuri all'interno del corpo eliminerebbe l'esecuzione simultanea di blocchi di codice racchiusi dai futures e condurrebbe al codice sequenziale. Lo vedi nell'esempio:

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

Il valore racchiuso dall'oggetto result1 sarebbe sempre negativo mentre il result2 sarebbe positivo.

Per maggiori dettagli sulla comprensione e sulla yield in generale, vedi http://docs.scala-lang.org/tutorials/FAQ/yield.html



Modified text is an extract of the original Stack Overflow Documentation
Autorizzato sotto CC BY-SA 3.0
Non affiliato con Stack Overflow