Scala-计划的未来

Scala-计划的未来,scala,future,Scala,Future,我正在尝试在Scala中实现预定的未来。我希望它等待特定的时间,然后执行身体。到目前为止,我尝试了以下简单的方法 val d = 5.seconds.fromNow val f = future {Await.ready(Promise().future, d.timeLeft); 1} val res = Await.result(f, Duration.Inf) 但我对未来有时间限制。这是正确的方法还是我应该简单地使用Java中的ScheduledExecutor?您可以将代码更改为以

我正在尝试在Scala中实现预定的未来。我希望它等待特定的时间,然后执行身体。到目前为止,我尝试了以下简单的方法

val d = 5.seconds.fromNow

val f = future {Await.ready(Promise().future, d.timeLeft); 1}

val res = Await.result(f, Duration.Inf)

但我对未来有时间限制。这是正确的方法还是我应该简单地使用Java中的ScheduledExecutor?

您可以将代码更改为以下内容:

val d = 5.seconds.fromNow
val f = Future {delay(d); 1}
val res = Await.result(f, Duration.Inf)

def delay(dur:Deadline) = {
  Try(Await.ready(Promise().future, dur.timeLeft))
}
import scala.concurrent.duration._
import scala.concurrent.ExecutionContext.Implicits._

DelayedFuture( 5 seconds )( println("Hello") )

但我不推荐。这样做,您将在将来阻塞(阻塞以等待永远不会完成的
承诺),我认为
执行上下文中的阻塞是非常不受鼓励的。我可以按照您所说的使用java scheduled executor,也可以按照@alex23的建议使用Akka。

仅使用标准库是无法做到这一点的。 对于大多数简单的用例,您可以使用如下小助手:

object DelayedFuture {
  import java.util.{Timer, TimerTask}
  import java.util.Date
  import scala.concurrent._
  import scala.concurrent.duration.FiniteDuration
  import scala.util.Try

  private val timer = new Timer(true)

  private def makeTask[T]( body: => T )( schedule: TimerTask => Unit )(implicit ctx: ExecutionContext): Future[T] = {
    val prom = Promise[T]()
    schedule(
      new TimerTask{
        def run() {
          // IMPORTANT: The timer task just starts the execution on the passed
          // ExecutionContext and is thus almost instantaneous (making it 
          // practical to use a single  Timer - hence a single background thread).
          ctx.execute( 
            new Runnable {
              def run() {
                prom.complete(Try(body))
              }
            }
          )
        }
      }
    )
    prom.future
  }
  def apply[T]( delay: Long )( body: => T )(implicit ctx: ExecutionContext): Future[T] = {
    makeTask( body )( timer.schedule( _, delay ) )
  }
  def apply[T]( date: Date )( body: => T )(implicit ctx: ExecutionContext): Future[T] = {
    makeTask( body )( timer.schedule( _, date ) )
  }
  def apply[T]( delay: FiniteDuration )( body: => T )(implicit ctx: ExecutionContext): Future[T] = {
    makeTask( body )( timer.schedule( _, delay.toMillis ) )
  }
}
可以这样使用:

val d = 5.seconds.fromNow
val f = Future {delay(d); 1}
val res = Await.result(f, Duration.Inf)

def delay(dur:Deadline) = {
  Try(Await.ready(Promise().future, dur.timeLeft))
}
import scala.concurrent.duration._
import scala.concurrent.ExecutionContext.Implicits._

DelayedFuture( 5 seconds )( println("Hello") )

请注意,与java scheduled futures不同,此实现不允许您取消未来。

我的解决方案与Régis非常相似,但我使用Akka来安排:

 def delayedFuture[T](delay: FiniteDuration)(block: => T)(implicit executor : ExecutionContext): Future[T] = {
    val promise = Promise[T]

    Akka.system.scheduler.scheduleOnce(delay) {
      try {
        val result = block
        promise.complete(Success(result))
      } catch {
        case t: Throwable => promise.failure(t)
      }
    }
    promise.future
  }
Akka有Akka.pattern:

def after[T](duration: FiniteDuration, using: Scheduler)(value: ⇒ Future[T])(implicit ec: ExecutionContext): Future[T]
“返回一个scala.concurrent.Future,在指定的持续时间后,该值将在提供的值成功或失败的情况下完成。”


最短的解决方案可能是使用scala async:

import scala.async.Async.{async, await}

def delay[T](value: T, t: duration): Future[T] = async {
  Thread.sleep(t.toMillis)
  value
}
或者,如果您希望延迟执行块

def delay[T](t: duration)(block: => T): Future[T] async {
  Thread.sleep(t.toMillis)
  block()
}

所有其他解决方案在每个延迟任务中使用akka或阻塞线程。更好的解决方案(除非您已经在使用akka)是使用java的ScheduledThreadPoolExecutor。下面是一个scala包装器的示例:


如果您希望在没有Akka的情况下安排完成,可以使用常规Java计时器安排完成承诺:

def delay[T](delay: Long)(block: => T): Future[T] = {
  val promise = Promise[T]()
  val t = new Timer()
  t.schedule(new TimerTask {
    override def run(): Unit = {
      promise.complete(Try(block))
    }
  }, delay)
  promise.future
}


Await.ready
使用
阻塞
,因此,如果您在五秒钟内进行工作,至少底层池可以为它启动一个线程。此(或原始问题)工作所需的导入将很好地看到。我认为它们是scala.concurrent.duration.u和scala.concurrent.u这可以在没有参与者系统的情况下使用吗?真的不用说为什么这对并发性很好这是useless@matanster你是怎么想的?当然,但这不是问题的一部分。一个如何使用的示例将是handle。这叫什么?作为调度程序应该通过什么?我的回答显示了如何在没有akka或阻止任何线程的情况下完成它,并在一年多前发布。谢谢我一定错过了你的答案,我喜欢它,在我的解决方案中,很少需要执行线程池。这是一个非常不充分的解决方案。如前所述,这将阻止线程。有两件事:在这两种情况下,您是否都缺少嵌套的wait{…}?这将阻止线程阻塞并导致实际的延迟。或者,为什么不用Thread.sleep(…)调用包装在阻塞{…}块中来构造Future,以防止潜在的死锁?我不明白一件事:有一个
隐式ctx:ExecutionContext
参数要应用,但是我看不到它会被用在哪里-我看不到
makeTask
timer.schedule
期待它。我有一个更严重的问题,实际上我确实需要传递ExecutionContext。罪魁祸首就在别处:我应该在ExecutionContext上运行body,而不是在计时器的后台线程上运行它(这意味着在整个应用程序范围内,对于每个延迟的未来,实际上只有一个线程,这并不好)。即使它本质上只是一个例子,但对我来说也是草率的。新版本更适合生产。不如使用
def run():Unit=prom.complete(Try(block())
代替手动异常包装?Scala()中支持一次您的解决方案有望变得更加清晰和简短。我注意到,
private val timer=new timer
使用默认值,即不作为deamon线程运行,因此可能会使用此代码停止进程的关闭以安排偶尔的任务?也许最好使用
private val timer=new timer(true)
来防止进程挂起?您确定语法吗?我认为
block
不是一个函数,它不应该用作
block()
,而应该用作
block
。此外,与我的评论类似-你是否在某处使用
executor
。@Suma这就是我不用测试就可以从内存中编写代码的原因。我已经修复了代码,并确保它符合我声称的那样,每次创建一个新的
计时器
实例可能不是一个好主意