Futures and Promises / Фьючи и Промисы: Promises

Английский оригинал Перевод на русский

Сожалеем, но текст оригинала доступен только зарегистрированным пользователям.

До сих пор мы рассматривали только объекты Future создаваемые асинхронными вычислениями начинающимися при помощи метода future. Однако, фьючи могут быть созданы используя промисы.

2heoh 08.10.13 в 06:37

0

В то время как фьючи определяются как вид объекта-плейсхолдера для результата, доступный только для чтения, который еще не существует, промис может быть определен как однократно записываемый контейнер который заполняет фьючу. Поэтому промис может быть использован для успешного разрешения фьючи со значением ("заполняющим" промис) при помощи метода success. И наоборот промис может быть использован для разрешения фьючи с исключением, _переводя_промис_в_состояние_ошибки_, используя метод failure.

2heoh 08.10.13 в 06:49

0

Промис p разрешает фьючу возвращая p.future. Эта фьюча характерна для промиса p. В зависимости от реализации возможна ситуация когда результат p.future эквивалентен p.

2heoh 08.10.13 в 06:52

0

Рассмотрим следующий пример источник(producer) -потребитель(consumer), в котором одно вычисление создает значение, и передает его в другое вычисление которое получает это значение. Эта передача значения выполняется при помощи промиса.

2heoh 08.10.13 в 06:57

0

import scala.concurrent.{ future, promise }
import scala.concurrent.ExecutionContext.Implicits.global
val p = promise[T]
val f = p.future
val producer = future {
val r = produceSomething()
p success r
continueDoingSomethingUnrelated()
}
val consumer = future {
startDoingSomething()
f onSuccess {
case r => doSomethingWithResult()
}
}

2heoh 09.10.13 в 12:38

0

Здесь мы создаем промис и вызываем его метод future для получения Future, которая его разрешает. Затем мы начинаем два асинхронных вычисления. Первое делает некоторое вычисления помещая его результат в r, которое затем используется для разрешения фьючи f, выполняя промис p. Второе производит некоторое вычисление и затем читает результат r разрешенной фьючи f. Обратите внимание, что потребитель может получить результат до того как источник закончит исполнение метода continueDoingSomethingUnrelated()

2heoh 08.10.13 в 07:19

0

Как было замечено ранее, промисы имеют семинтику одинократного присвоения. Поэтому они могут быть выполнены только один раз. При вызывая success на промисе который уже был разрешен (или содержит ошибку) будет вызвано исключение IllegalStateException.

2heoh 08.10.13 в 07:47

0

Следующий пример показывает как промис можно завершить ошибкой.

2heoh 08.10.13 в 07:49

0

Здесь источник вычисляет промежуточный результат r и проверяет его корректность. В случае если он некорректный промис переводится в ошибочное состояние, это производится разрешением промиса p исключением. В этом случае связанная фьюча f также разрешается ошибкой. Иначе, источник продолжает вычисление и разрешает фьючу f корректным результатом, разрешая промис p.

2heoh 08.10.13 в 08:00

0

Также промисы могут быть разрешены при помощи метода complete, который берет возможное значение Try[T], которое представляет собой или ошибочный результат типа Failure[Throwable] или успешный результат типа Success[T].

2heoh 08.10.13 в 08:04

0

Также как и в случае успешного результата, вызов метода failure на уже разрешенном промисе вызовет исключение IllegalStateException.

2heoh 08.10.13 в 08:06

0

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

2heoh 08.10.13 в 08:12

0

В некоторых случаях клиенты могут захотеть разрешить промис только если он еще не завершен (например есть несколько HTTP-запросов которые выполняются из нескольких различных фьюч и клиентская программа хочет получить только первый HTTP ответ, соответствующий первой фьюче которая разрешает промис). Для таких случаев есть методы tryComplete, trySuccess и tryFailure. Клиент должен избегать использования этих методов в не-детерминистких программах (т.е. которые зависят от стадии выполнения).

2heoh 08.10.13 в 08:19

0

Метод completeWith разрешает промис другой фьючей. После того как фьюча разрешена, промис, в свою очередь, получает разрешение в виде результата этой фьчи. Следующий результат печатает 1:

2heoh 08.10.13 в 08:23

0

Завершить промис исключением можно при помощи трех подтипов Throwable. Если используется Throwable класса scala.runtime.NonLocalReturnControl для того чтобы прервать промис, то он завершится с соответствующим значением. В том случае если используется Throwable чтобы прервать промис является сущностью Error, InterruptedException или scala.util.control.ControlThrowable тогда исключение обрачивается как источник ExecutionException что в свою очередь переводит промис в ошибочное состояние.

2heoh 09.10.13 в 12:25

0

При использовании промисов, метод onComplete фьючи или конструктор фьючи вы можете использовать любую функциональную конструкцию комбинаторов, которые описаны ранее. Предположим вы хотите сделать новый комбинатор который сначала берет 2 фьючи f и g и создает новую фьючу которая завершается когда какая-нибудь из них разрешается (в зависимости от того какая раньше), но только при условии что она успешная.

2heoh 09.10.13 в 12:32

0

Вот пример как это сделать:

2heoh 09.10.13 в 12:32

0

Обратите внимание, что в данной реализации при случае если ни f ни g не завершатся успехом, то first(f, g) никогда не завершится (ни со значением, ни с исключением)

2heoh 09.10.13 в 12:36

0
Минутку...