Как отменить будущее в Scala?
Java Future имеет метод cancel
, который может прерывать поток, который запускает задачу Future
. Например, если я завершу прерывистый блокирующий вызов в Java Future
, я могу прервать его позже.
Scala Будущее не предоставляет метод cancel
. Предположим, что я завершаю прерывистый блокирующий вызов в Scala Future
. Как я могу его прервать?
Ответы
Ответ 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)
}