web-dev-qa-db-fra.com

Comment annuler Future in Scala?

Java Future a la méthode cancel, qui peut interrompre le thread, qui exécute la tâche Future. Par exemple, si j'encapsule un appel de blocage interruptible dans un Java Future Je peux l'interrompre plus tard.

Scala Future ne fournit aucune méthode cancel. Supposons que j'encapsule un appel interruptible bloquant dans un Scala Future. Comment puis-je l'interrompre?

52
Michael

Cela ne fait pas encore partie de l'API Futures, mais pourrait être ajouté en tant qu'extension à l'avenir.

Comme solution de contournement, vous pouvez utiliser le firstCompletedOf pour envelopper 2 futures - le futur que vous souhaitez annuler et un futur qui provient d'un Promise personnalisé. Vous pouvez alors annuler le futur ainsi créé en manquant à la promesse:

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

Vous pouvez maintenant l'appeler à tout moment pour obtenir un "wrapper annulable". Exemple de cas d'utilisation:

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

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

ÉDITER:

Pour une discussion détaillée sur l'annulation, voir le chapitre 4 du livre Apprentissage de la programmation simultanée dans Scala .

30
axel22

Je n'ai pas testé cela, mais cela élargit la réponse de Pablo Francisco Pérez Hidalgo. Au lieu de bloquer en attendant le Java Future, nous utilisons à la place un Promise intermédiaire.

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)
}
9
nightingale

En annulant, je suppose que vous souhaitez interrompre violemment le future.

Trouvé ce segment de code: https://Gist.github.com/viktorklang/5409467

A fait quelques tests et semble bien fonctionner!

Prendre plaisir :)

8

Je pense qu'il est possible de réduire la complexité des implémentations fournies en utilisant Java 7 Future interface et ses implémentations.

Cancellable peut construire un Java futur qui est celui qui doit être annulé par sa méthode cancel. Un autre futur peut attendre sa fin, devenant ainsi l'interface observable qui est lui-même immuable dans l'état:

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