Задержка выполнения функции

Каков самый простой способ отложить выполнение функции в Scala, что-то вроде setTimeout в JavaScript? В идеале без порождения потока для отложенного выполнения, т.е. последовательного выполнения. Самое близкое, что мне удалось найти, это Scheduler, но это перебор.

В целях тестирования я открываю тысячи подключений, затем они получают ответы через 10 секунд. В node.js это выглядит так:

http.createServer(function (req, res) {
  res.writeHead(200, {'Content-Type': 'text/plain'});
  setTimeout(function() {res.end('Hello World\n');}, 10000 );
}).listen(8080, '127.0.0.1');

Но что будет ближайшей версией Scala, делающей то же самое? Мне все равно, будет ли res.end выполняться в нескольких потоках или стоять в очереди в одном.


person Oleg Mikheev    schedule 22.06.2013    source источник
comment
Возможный дубликат, я не уверен, что этот ответ поможет: stackoverflow.com/a/16629357/1296806, но, возможно, если вы хотите бесполезный одноразовый для тестирования.   -  person som-snytt    schedule 22.06.2013
comment
Вы также можете взглянуть на этот другой вопрос:   -  person Régis Jean-Gilles    schedule 22.06.2013


Ответы (1)


Устали от критики за слишком простой ответ на вопрос, вот стандартные идиомы JVM:

$ scala
Welcome to Scala 2.11.8 (Java HotSpot(TM) 64-Bit Server VM, Java 1.6.0_65).
Type in expressions for evaluation. Or try :help.

scala> import java.util.{Timer,TimerTask}
import java.util.{Timer, TimerTask}

scala> val timer = new Timer
timer: java.util.Timer = java.util.Timer@2d9ffd6f

scala> def delay(f: () => Unit, n: Long) = timer.schedule(new TimerTask() { def run = f() }, n)
delay: (f: () => Unit, n: Long)Unit

scala> delay(() => println("Done"), 1000L)

scala> Done


scala> import java.util.concurrent._
import java.util.concurrent._

scala> val x = Executors.newScheduledThreadPool(2)
x: java.util.concurrent.ScheduledExecutorService = java.util.concurrent.ScheduledThreadPoolExecutor@2c5d529e

scala> x.schedule(new Callable[Int]() { def call = { println("Ran"); 42 }}, 1L, TimeUnit.SECONDS)
res3: java.util.concurrent.ScheduledFuture[Int] = java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask@3ab0f534

scala> Ran

В стандартной библиотеке нет API для планирования отложенной задачи, но вы можете сделать ExecutionContext с фиксированной задержкой, чтобы использовать Scala Future.

scala> import scala.concurrent._
import scala.concurrent._

scala> implicit val xx = new ExecutionContext() {
     | def reportFailure(t: Throwable) = t.printStackTrace()
     | def execute(r: Runnable) = x.schedule(new Callable[Unit]() { def call = r.run() }, 1L, TimeUnit.SECONDS)
     | }
xx: scala.concurrent.ExecutionContext = $anon$1@40d3ab8b

scala> Future(println("hello"))
res4: scala.concurrent.Future[Unit] = List()

scala> hello

scala> Future(42)
res5: scala.concurrent.Future[Int] = List()                

scala> .value
res6: Option[scala.util.Try[Int]] = Some(Success(42))

Или вы можете использовать планировщик Akka, который является каноническим ответом в Scheduled Executor в Scala.

Старый однострочный:

Простейшее по-прежнему просто future { blocking(Thread.sleep(10000L)); "done" }

но я хотел разместить рекламу этого парня, на которого я только что наткнулся, который дает вам индикатор прогресса или промежуточное значение. Я бы хотел, чтобы у него было другое имя, вот и все.

scala> import concurrent._
import concurrent._

scala> import ExecutionContext.Implicits._
import ExecutionContext.Implicits._

scala> import duration._
import duration._

scala> val deadline = 60.seconds.fromNow
deadline: scala.concurrent.duration.Deadline = Deadline(38794983852399 nanoseconds)

scala> new DelayedLazyVal(() => deadline.timeLeft.max(Duration.Zero), blocking {
     | Thread.sleep(deadline.timeLeft.toMillis)
     | Console println "Working!"
     | })
res9: scala.concurrent.DelayedLazyVal[scala.concurrent.duration.FiniteDuration] = scala.concurrent.DelayedLazyVal@50b56ef3

scala> res9()
res10: scala.concurrent.duration.FiniteDuration = 23137149130 nanoseconds

scala> res9.isDone
res11: Boolean = false

scala> res9()
res12: scala.concurrent.duration.FiniteDuration = 12499910694 nanoseconds

scala> res9()
res13: scala.concurrent.duration.FiniteDuration = 5232807506 nanoseconds

scala> Working!


scala> res9.isDone
res14: Boolean = true

scala> res9()
res15: scala.concurrent.duration.FiniteDuration = 0 days

Ниже приведена альтернативная формулировка с Both для вычисления значения после задержки. Используя Left, конечно, когда еще есть время Left.

scala> new DelayedLazyVal(()=> if (deadline.hasTimeLeft) Left(deadline.timeLeft) else
     | Right("Working!"), blocking(Thread.sleep(deadline.timeLeft.toMillis)))
res21: scala.concurrent.DelayedLazyVal[Product with Serializable with scala.util.Either[scala.concurrent.duration.FiniteDuration,String]] = scala.concurrent.DelayedLazyVal@78f9c6f2

scala> res21()
res22: Product with Serializable with scala.util.Either[scala.concurrent.duration.FiniteDuration,String] = Left(28553649064 nanoseconds)

scala> res21()
res23: Product with Serializable with scala.util.Either[scala.concurrent.duration.FiniteDuration,String] = Left(9378334087 nanoseconds)

scala> res21.isDone
res24: Boolean = false

scala> res21()
res25: Product with Serializable with scala.util.Either[scala.concurrent.duration.FiniteDuration,String] = Right(Working!)

scala> res21.isDone
res26: Boolean = true
person som-snytt    schedule 23.06.2013
comment
не приведет ли это к тому, что тысячи потоков будут спать в течение 10 секунд? - person Oleg Mikheev; 24.06.2013
comment
Есть ли неблокирующее решение? - person Andy Hayden; 15.04.2016