Как отменить будущее в Scala? - программирование
Подтвердить что ты не робот

Как отменить будущее в Scala?

Java Future имеет метод cancel, который может прерывать поток, который запускает задачу Future. Например, если я завершу прерывистый блокирующий вызов в Java Future, я могу прервать его позже.

Scala Будущее не предоставляет метод cancel. Предположим, что я завершаю прерывистый блокирующий вызов в Scala Future. Как я могу его прервать?

4b9b3361

Ответ 1

Это еще не часть API Future, но может быть добавлено как расширение в будущем.

В качестве обходного пути вы можете использовать firstCompletedOf для обертывания 2 фьючерсов - будущее, которое вы хотите отменить, и будущее, которое приходит из пользовательского Promise. Затем вы можете отменить созданное таким образом будущее, не выполнив обещание:

def cancellable[T](f: Future[T])(customCode: => Unit): (() => Unit, Future[T]) = {
  val p = Promise[T]
  val first = Future firstCompletedOf Seq(p.future, f)
  val cancellation: () => Unit = {
    () =>
      first onFailure { case e => customCode}
      p failure new Exception
  }
  (cancellation, first)
}

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

val f = callReturningAFuture()
val (cancel, f1) = cancellable(f) {
  cancelTheCallReturningAFuture()
}

// somewhere else in code
if (condition) cancel() else println(Await.result(f1))

EDIT:

Подробное обсуждение отмены, см. главу 4 в Обучение параллельному программированию в книге Scala.

Ответ 2

Я не тестировал это, но это расширилось на ответ Пабло Франсиско Переса Идальго. Вместо блокировки ожидания java Future вместо этого мы используем промежуточный Promise.

import java.util.concurrent.{Callable, FutureTask}
import scala.concurrent.{ExecutionContext, Promise}
import scala.util.Try

class Cancellable[T](executionContext: ExecutionContext, todo: => T) {
  private val promise = Promise[T]()

  def future = promise.future

  private val jf: FutureTask[T] = new FutureTask[T](
    new Callable[T] {
      override def call(): T = todo
    }
  ) {
    override def done() = promise.complete(Try(get()))
  }

  def cancel(): Unit = jf.cancel(true)

  executionContext.execute(jf)
}

object Cancellable {
  def apply[T](todo: => T)(implicit executionContext: ExecutionContext): Cancellable[T] =
    new Cancellable[T](executionContext, todo)
}

Ответ 3

Отменив, я думаю, вы хотели бы насильственно прервать future.

Нашел этот сегмент кода: https://gist.github.com/viktorklang/5409467

Сделал несколько тестов и, кажется, работает нормально!

Наслаждайтесь:)

Ответ 4

Я думаю, что можно уменьшить сложность реализаций, предоставляемых с помощью Java 7 Future interface и его реализаций.

Cancellable может построить будущее Java, которое будет отменено его методом cancel. Другое будущее может дождаться его завершения, став наблюдаемым интерфейсом, который сам по себе неизменен в состоянии:

 class Cancellable[T](executionContext: ExecutionContext, todo: => T) {

   private val jf: FutureTask[T] = new FutureTask[T](
     new Callable[T] {
       override def call(): T = todo
     }
   )

   executionContext.execute(jf)

   implicit val _: ExecutionContext = executionContext

   val future: Future[T] = Future {
     jf.get
   }

   def cancel(): Unit = jf.cancel(true)

 }

 object Cancellable {
   def apply[T](todo: => T)(implicit executionContext: ExecutionContext): Cancellable[T] =
     new Cancellable[T](executionContext, todo)
 }