Поиск…


Создание будущего

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

Весьма просто, метод divide создает Будущее, которое будет разрешено с частным a над b .

Потребление успешного будущего

Самый простой способ использовать успешное будущее - или, скорее, получить ценность в будущем - это использовать метод map . Предположим , что некоторый код вызывает divide метод FutureDivider объекта из «Создание будущего». Например , Что бы код должен выглядеть , чтобы получить частное a течение b ?

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

Потребление неудачного будущего

Иногда вычисление в Будущем может создать исключение, которое приведет к сбою Будущего. В примере «Создание будущего», если код вызова передал 55 и 0 методу divide ? Разумеется, это приведет к ArithmeticException после попытки разделить на ноль. Как это будет обрабатываться во потребляющем коде? На самом деле существует несколько способов устранения сбоев.

Обработать исключение с помощью recover и сопоставления шаблонов.

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

Обрабатывать исключение с failed проекцией, где исключение становится значением будущего:

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

Объединение будущего

В предыдущих примерах были продемонстрированы индивидуальные особенности будущего, обработка успешных и неудачных случаев. Обычно, однако, обе функции обрабатываются гораздо более кратко. Вот пример, написанный более аккуратным и реалистичным образом:

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

Секвенирование и перемещение фьючерсов

В некоторых случаях необходимо вычислить переменную величину значений для отдельных фьючерсов. Предположим, что у вас есть List[Future[Int]] , но вместо этого нужно обработать List[Int] . Затем возникает вопрос, как превратить этот экземпляр List[Future[Int]] в Future[List[Int]] . Для этого существует метод sequence на объекте компаньона Future .

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

В общем случае sequence является широко известным оператором в мире функционального программирования , который преобразует F[G[T]] в G[F[T]] с ограничениями на F и G .

Существует альтернативный оператор, называемый traverse , который работает аналогично, но принимает функцию в качестве дополнительного аргумента. С помощью функции тождества x => x в качестве параметра она ведет себя как оператор sequence .

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

Однако дополнительный аргумент позволяет изменять каждый будущий экземпляр внутри данного listOfFuture . Кроме того, первый аргумент не должен быть списком Future . Поэтому можно преобразовать пример следующим образом:

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

В этом случае List(1,2,3) непосредственно передается в качестве первого аргумента, а функция тождества x => x заменяется функцией Future(_) чтобы аналогично обернуть каждое значение Int в Future . Преимущество этого заключается в том, что промежуточный List[Future[Int]] может быть опущен для повышения производительности.

Объединить несколько фьючерсов - для понимания

Понимание - это компактный способ запуска блока кода, который зависит от успешного результата нескольких фьючерсов.

С f1, f2, f3 три Future[String] , которые будут содержать строки one, two, three соответственно,

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

fCombined будет Future[String] содержащий строку one - two - three только все фьючерсы будут успешно завершены.

Обратите внимание, что здесь подразумевается неявный ExectionContext.

Кроме того, имейте в виду, что для понимания это просто синтаксический сахар для метода flatMap, поэтому построение объектов Future внутри тела исключило бы одновременное выполнение кодовых блоков, заключенных в фьючерсы, и привести к последовательному коду. Вы видите это на примере:

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

Значение, заключенное в result1 объекта result1 , всегда будет отрицательным, а result2 - положительным.

Более подробную информацию о понимании и yield в целом см. По адресу http://docs.scala-lang.org/tutorials/FAQ/yield.html.



Modified text is an extract of the original Stack Overflow Documentation
Лицензировано согласно CC BY-SA 3.0
Не связан с Stack Overflow