Подтвердить что ты не робот

Требуется уточнение фьючерсов и promises в Scala

Я пытаюсь обойти Scala обещание и будущие конструкции.

Я читал Фьючерсы и Promises в документации Scala и немного смущен, поскольку у меня возникает ощущение, что понятия promises и фьючерсы смешиваются.

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

В Scala мы можем получить результат, используя присоединенные обратные вызовы к будущему.

Где я потерял, как обещание имеет будущее?

Я читал об этих понятиях в Clojure тоже, полагая, что обещание и будущее имеют общую общую концепцию, но похоже, что я был неправ.

Обещание p завершает будущее, возвращаемое p.future. Это будущее конкретно к обещанию. В зависимости от реализации это может быть случай, когда p.future eq p.

val p = promise[T]
val f = p.future
4b9b3361

Ответ 1

Вы можете думать о фьючерсах и promises как о двух разных сторонах трубы. С точки зрения обещания, данные вставляются, а с другой стороны, данные могут быть извлечены.

И будущее - это своего рода асинхронная операция, которая будет завершена в другом пути выполнения.

На самом деле, будущее является объектом-заполнителем для значения, которое может быть доступно в какой-то момент времени асинхронно. Это не асинхронное вычисление.

Тот факт, что существует конструктор будущего future, который возвращает такой объект-заполнитель и порождает асинхронное вычисление, которое завершает этот объект-заполнитель, не означает, что асинхронное вычисление называется будущим. Существуют и другие методы будущих конструкторов /factory.

Но я не понимаю, как обещает будущее?

Разделить promises и фьючерсы на 2 отдельных интерфейса было конструктивным решением. Вы могли бы иметь эти два под тем же интерфейсом future, но тогда это позволило бы клиентам фьючерсов завершить их, а не предполагаемое дополнение к будущему. Это может вызвать непредвиденные ошибки, так как может быть любое количество конкурирующих дополнений.

например. для асинхронного вычисления, порожденного конструкцией future, было бы неясно, должно ли оно выполнить обещание или если клиент сделает это.

Фьючерсы и promises предназначены для ограничения потока данных в программе. Идея заключается в том, чтобы клиент, который подписывается на данные, будет действовать после его поступления. Роль обещающего клиента заключается в предоставлении этих данных. Смешивание этих двух ролей может привести к программам, которые сложнее понять или обосновать.

Вы также можете спросить, почему черта Promise не распространяется future. Это еще одно дизайнерское решение, чтобы препятствовать программистам слепо передавать Promise клиентам, где они должны повышать рейтинг Promise до future (этот опрос считается неприемлемым, тогда как для явного вызова future на обещание обеспечивается вы называете это каждый раз). Другими словами, возвращая обещание, которое вы даете право на его завершение кому-то еще, и, возвращая будущее, вы даете право подписаться на него.

EDIT:

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

Ответ 2

Разница между ними заключается в том, что фьючерсы обычно сосредоточены вокруг вычисления, а promises сосредоточены вокруг данных.

Кажется, ваше понимание соответствует этому, но позвольте мне объяснить, что я имею в виду:

В обоих scala и clojure фьючерсах (если они не возвращаются какой-либо другой функцией/методом), созданной с помощью некоторых вычислений:

// scala
future { do_something() }

;; clojure
(future (do-something))

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

Напротив, в обоих случаях promises представляют собой пустой контейнер, который позже может быть заполнен (ровно один раз):

// scala
val p = promise[Int]
...
p success 10 // or failure Exception()

;; clojure
(def p (promise))
(deliver p 10) 

Как только это так, его можно прочитать.

Чтение фьючерсов и promises выполняется через deref в clojure (и realized? может использоваться для проверки блокировки deref). В scala чтение выполняется с помощью методов, предоставляемых признаком Future. Чтобы прочитать результат обещания, мы, таким образом, должны получить объект, реализующий Future, это делается с помощью p.future. Теперь, если признак Future реализуется с помощью Promise, тогда p.future может возвращать this, а два равны. Это просто выбор реализации и не меняет понятия. Значит, вы не ошиблись! В любом случае Фьючерсы в основном рассматриваются с использованием обратных вызовов.

В этот момент может оказаться целесообразным пересмотреть начальную характеристику двух понятий:

Фьючерсы представляют собой вычисление, которое даст результат в какой-то момент. Давайте рассмотрим одну возможную реализацию: мы запускаем код в каком-то потоке (пуле), и после его завершения мы организуем использование возвращаемого значения для выполнения обещания. Поэтому чтение результата в будущем - это обещание; Это clojure способ мышления (не обязательно реализации).

С другой стороны, обещание представляет значение, которое будет заполнено в какой-то момент. Когда он заполняется, это означает, что некоторые вычисления дали результат. Таким образом, это похоже на завершение в будущем, поэтому мы должны использовать значение таким же образом, используя обратные вызовы; Это scala способ мышления.

Ответ 3

Обратите внимание, что под капотом Future реализуется в терминах Promise, и этот Promise завершается телом, который вы передали в свой Future:

def apply[T](body: =>T): Future[T] = impl.Future(body)  //here I have omitted the implicit ExecutorContext

impl.Future - это реализация признака Future:

def apply[T](body: =>T)(implicit executor: ExecutionContext): scala.concurrent.Future[T] =
{
  val runnable = new PromiseCompletingRunnable(body)
  executor.prepare.execute(runnable)
  runnable.promise.future
}

Где PromiseCompletingRunnable выглядит следующим образом:

class PromiseCompletingRunnable[T](body: => T) extends Runnable {
val promise = new Promise.DefaultPromise[T]()

override def run() = {
  promise complete {
    try Success(body) catch { case NonFatal(e) => Failure(e) }
  }
} }

Итак, вы видите, что хотя они представляют собой отдельные концепции, которые вы можете использовать самостоятельно, в действительности вы не можете получить Future без использования Promise.