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

Почему Scala Future не имеет метода .get/get (maxDuration), заставляя нас прибегать к Await.result()?

Есть ли какое-либо конкретное преимущество в развязывании метода get из класса Future (где я ожидаю, что он будет проживать) и вместо этого заставить кодера быть в курсе этого внешнего класса двух методов, называемого Await?

4b9b3361

Ответ 1

Есть ли какое-либо особое преимущество в развязывании метода get из класса Future

Да, чтобы разработчику было сложно делать что-то не так. A Future представляет собой вычисление, которое будет завершено в будущем и может быть недоступно в текущей точке вызова. Если вам нужно заблокировать будущее, почему бы не выполнить его синхронно? Какой смысл планировать его в threadpool, тратя впустую отличный поток threadpool?

Документация :

Блокировка вне будущего

Как упоминалось ранее, блокирование будущего сильно не поощряется ради производительности и предотвращения взаимоблокировок. Обратные вызовы и комбинаторы на фьючерсах являются предпочтительным способом использования их результатов. Однако блокировка может потребоваться в определенных ситуациях и поддерживается фьючерсами и API Promises.

И даже документация Await:

Иногда полезно, например, для тестирования рекомендуется избегать Await, когда это возможно, в пользу обратных вызовов и комбинаторов, таких как onComplete, и использовать их для понимания. Ожидание блокирует поток, на котором он выполняется, и может вызвать проблемы с производительностью и тупиком.

Вы можете видеть, что разработчики языка намеренно хотели этого эффекта.

Ответ 2

Для получения более подробной истории 1 для принятого ответа:

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

A Future представляет значение (или невозможность произвести указанное значение) оторван от времени.

Более конкретно означает, что Future[T] имеет строго меньшую информацию, чем простой T, как показано на рисунке:

//At the line of this comment, there is no `f`
val f: Future[T] = … //At the line of this comment, there is an `f` but not necessarily its value of type `T`.

В то время как:

//At the line of this comment, there is no `t`
val t: T = … //At the line of this comment, there is a `T`

Почему это важно, я слышу, как вы говорите. Ну, если вы хотите иметь метод от Future[T] до T, вы говорите: я получаю значение, которое может быть недоступно, и я вернусь после него.

Это означает, что метод должен будет "передать время", пока значение еще не доступно, это обычно влечет за собой блокировку текущего потока выполнения, и поскольку потоки выполнения являются дорогостоящим ресурсом, и обычно требуется время, чтобы войти в заблокированное состояние и снова вернуться обратно 3 этого следует избегать.

Как будто этого было недостаточно, очень легко попасть в тупиковые ситуации, где Futures не может завершить работу, поскольку они не получат Thread для выполнения, потому что все эти потоки застревают, пытаясь получить это значение Future.

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

Итак, мы решили продвигать стиль программирования, который не требует блокировки, например обратные вызовы 4 и преобразования 5 чтобы уменьшить риск взаимоблокировок и низкую производительность.

Мы полагали, что если мы должны полностью опустить возможность блокировать получение результата от Будущего, конечным результатом будет то, что каждый будет изобретать его и, возможно, с различным качеством.

Мы также обнаружили, что если бы мы могли ожидать конкретного , мы получили следующие преимущества:

  • API выглядит одинаково для всех Awaitable s
  • Легко проверить код для/против
  • Легко запретить/разрешить использование инструментария
  • Мы можем подключиться к понятию blocking, чтобы попытаться выполнить уклоняющие действия для смягчения взаимоблокировок, запретить его во время выполнения, зарегистрировать его, время и т.д.

Итак, у вас есть это!

Cheers, √

1 Будучи соавтором SIP-14 "Фьючерсы и Promises" и поддерживающим его с тех пор.

2 Насколько вам известно, когда дело доходит до API...

3 Задержка пробуждения нити может эффективно ограничивать обработку между 1k-10kops/s.

4 Мы также все более избегаем использования обратных вызовов, вместо того чтобы продвигать использование комбинаторов трансформации, см. this blog series Я написал для Scala 2.12 Фьючерсы.

5 Пример. map, flatMap, filter, recover, recoverWith, а совсем недавно transform и transformWith

Ответ 3

перейти на фьючерсы

В фьючерсах есть .get, начиная с первой реализации в стандартной версии Scala. Это несколько скрыто: .value.get

scala> val f = Future("test")
f: scala.concurrent.Future[String] = Success(test)

scala> f.value.get
res5: scala.util.Try[String] = Success(test)

Недостатком использования этого является то, что он будет генерировать исключение, если будущее еще не вернулось.

scala> Future("test").value.get
res7: scala.util.Try[String] = Success(test)

scala> Future("test").value.get
 java.util.NoSuchElementException: None.get
 at scala.None$.get(Option.scala:347)
 at scala.None$.get(Option.scala:345)
 ... 32 elided

Почему Await.result, а не get(timeToWait)?

Добавление к @YuvalsItzchakov отличного ответа: Идея использования Future i, чтобы сделать асинхронность видимой для системы типов и всех кодеров, показывающих, что с использованием этой концепции применяются специальные правила, и необходимо соблюдать осторожность. Поскольку это аппликативный функтор, у вас есть действительно простой способ работать со значениями, используя map или foreach, а также средства для их объединения с помощью map и flatMap.

Единственное, что по замыслу не встроено, - это то, как "unuture" a Future, сохраняя при этом специальные настраиваемые правила ожидания, зависящие от времени. Это примерно описано, что будет делать Await.result.