Sök…


Skapa en framtid

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

Helt enkelt den divide skapar metoden en framtid som kommer att lösa med kvoten av a över b .

Konsumerar en framgångsrik framtid

Det enklaste sättet att konsumera en framgångsrik Future-- eller snarare få värdet inuti Future-- är att använda map metoden. Antag lite kod anropar divide metoden enligt FutureDivider objektet från "Skapa en framtid" exempel. Hur skulle koden behöva se ut för att få kvoten på a över b ?

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

Konsumerar en misslyckad framtid

Ibland kan beräkningen i en framtid skapa ett undantag, vilket gör att framtiden misslyckas. I exemplet "Skapa en framtid", vad händer om anropskoden passerade 55 och 0 till divide ? Det skulle kasta en ArithmeticException efter att ha försökt dela med noll, naturligtvis. Hur skulle det hanteras i konsumtionskod? Det finns faktiskt en handfull sätt att hantera misslyckanden.

Hantera undantaget med recover och mönstermatchning.

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

Hantera undantaget med den failed projektionen, där undantaget blir värdet för framtiden:

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

Sätta framtiden tillsammans

De tidigare exemplen visade de enskilda funktionerna i en framtid, hanterar framgångar och misslyckade fall. Vanligtvis hanteras emellertid båda funktionerna mycket mer utsträckt. Här är exemplet, skrivet på ett snyggare och mer realistiskt sätt:

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

Sekvensering och korsning av Futures

I vissa fall är det nödvändigt att beräkna en variabel mängd värden på separata Futures. Antag att du har en List[Future[Int]] , men istället måste en List[Int] bearbetas. Då är frågan hur man förvandlar den här instansen av List[Future[Int]] till en Future[List[Int]] . För detta ändamål finns det sequenceFuture companion-objektet.

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

I allmänhet är sequence en vanligt känd operatör inom världen av funktionell programmering som omvandlar F[G[T]] till G[F[T]] med begränsningar till F och G

Det finns en alternativ operatör som heter traverse , som fungerar på liknande sätt men tar en funktion som ett extra argument. Med identitetsfunktionen x => x som en parameter uppträder den som sequence .

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

Det extra argumentet tillåter dock att modifiera varje framtida instans inom den givna listOfFuture . Dessutom behöver det första argumentet inte vara en lista över Future . Därför är det möjligt att omvandla exemplet enligt följande:

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

I det här fallet överförs List(1,2,3) direkt som första argument och identitetsfunktionen x => x ersätts med funktionen Future(_) att på liknande sätt linda in varje Int värde till en Future . En fördel med detta är att mellanhandslistan List[Future[Int]] kan utelämnas för att förbättra prestandan.

Kombinera flera framtider - för förståelse

Förståelsen är ett kompakt sätt att köra ett kodblock som beror på det framgångsrika resultatet av flera framtider.

Med f1, f2, f3 tre Future[String] : er som kommer att innehålla strängarna respektive, one, two, three ,

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

fCombined kommer att vara en Future[String] innehåller strängen one - two - three när alla futures har avslutats.

Observera att en implicit ExectionContext antas här.

Tänk också på att för förståelse bara är ett syntaktiskt socker för en plattMapp-metod, så framtida föremålens konstruktion inuti för kroppen skulle eliminera samtidigt exekvering av kodblock som är inneslutna av futures och leder till sekventiell kod. Du ser det på exempel:

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

Värde omslutet av result1 objektet skulle alltid vara negativt medan result2 skulle vara positivt.

För mer information om förståelse och yield i allmänhet, se http://docs.scala-lang.org/tutorials/FAQ/yield.html



Modified text is an extract of the original Stack Overflow Documentation
Licensierat under CC BY-SA 3.0
Inte anslutet till Stack Overflow