如何将基于时间的观察者添加到Scala Future?

时间:2016-06-09 22:17:14

标签: scala scala-2.11 scala-2.12

我想在Scala after(d: FiniteDuration)(callback: => Unit)中添加一个Future util,这样我就能做到这一点:

val f = Future(someTask)

f.after(30.seconds) {
  println("f has not completed in 30 seconds!")
}

f.after(60.seconds) {
  println("f has not completed in 60 seconds!")
}

我该怎么做?

4 个答案:

答案 0 :(得分:1)

通常我使用线程池执行器并承诺:

import scala.concurrent.duration._
import java.util.concurrent.{Executors, ScheduledThreadPoolExecutor}
import scala.concurrent.{Future, Promise}

val f: Future[Int] = ???

val executor = new ScheduledThreadPoolExecutor(2, Executors.defaultThreadFactory(), AbortPolicy)

def withDelay[T](operation: ⇒ T)(by: FiniteDuration): Future[T] = {
  val promise = Promise[T]()
  executor.schedule(new Runnable {
    override def run() = {
      promise.complete(Try(operation))
    }
  }, by.length, by.unit)
  promise.future
}

Future.firstCompletedOf(Seq(f, withDelay(println("still going"))(30 seconds)))
Future.firstCompletedOf(Seq(f, withDelay(println("still still going"))(60 seconds)))

答案 1 :(得分:0)

一种方法是使用Future.firstCompletedOf(请参阅此blogpost):

val timeoutFuture = Future { Thread.sleep(500); throw new TimeoutException }

val f = Future.firstCompletedOf(List(f, timeoutFuture))
f.map { case e: TimeoutException => println("f has not completed in 0.5 seconds!") }

其中TimeoutException是一些例外或类型。

答案 2 :(得分:0)

使用import akka.pattern.after。如果你想在没有akka的情况下实现它是source code。另一个(java)示例是TimeoutFuture中的com.google.common.util.concurrent

答案 3 :(得分:0)

这样的事情,也许是:

  object PimpMyFuture {
     implicit class PimpedFuture[T](val f: Future[T]) extends AnyVal {
        def after(delay: FiniteDuration)(callback: => Unit): Future[T] = {
           Future {
             blocking { Await.ready(f, delay) }
           } recover { case _: TimeoutException => callback }
           f
        }
     } 
  }

  import PimpMyFuture._
  Future { Thread.sleep(10000); println ("Done") }
    .after(5.seconds) { println("Still going") }

这个实现很简单,但它基本上使你需要的线程数增加一倍 - 每个活跃的未来有效地占用两个线程 - 这有点浪费。或者,您可以使用计划任务使您的等待无阻塞。我不知道"标准" scala中的scheduler(每个lib都有自己的),但对于像这样的简单任务,你可以直接使用java TimerTask

object PimpMyFutureNonBlocking {    
 val timer = new java.util.Timer

 implicit class PimpedFuture[T](val f: Future[T]) extends AnyVal {
    def after(delay: FiniteDuration)(callback: => Unit): Future[T] = {
       val task = new java.util.TimerTask {
          def run() { if(!f.isCompleted) callback }
       }
       timer.schedule(task, delay.toMillis)
       f.onComplete { _ => task.cancel }
       f
    }
  } 
}