2013-06-22 9 views
16

Qual è il modo più semplice per ritardare l'esecuzione della funzione in Scala, ad esempio JavaScript setTimeout? Idealmente senza thread di generazione per esecuzione ritardata, cioè esecuzione sequenziale. Il più vicino che sono stato in grado di trovare era Akka Scheduler, ma questo è un eccesso.Esecuzione funzione ritardata

Per i miei scopi di test sto aprendo migliaia di connessioni, quindi ottengono risposte in 10 secondi. In node.js sembra:

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'); 

Ma quale sarebbe la versione Scala più vicina di fare lo stesso? Non mi interessa se res.end verrà eseguito in più thread o in coda in uno solo.

+1

Eventuali duplicati, io non sono sicuro che questa risposta aiuta: http://stackoverflow.com/a/16629357/1296806 ma forse se si vuole un junky una tantum per il test. –

+0

Puoi anche dare un'occhiata a questa altra domanda: http://stackoverflow.com/questions/16359849/scala-scheduledfuture –

risposta

20

Stanco di ottenere antiproiettile per rispondere alla domanda per il più semplice troppo semplice, ecco i idiomi JVM standard:

$ 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 = [email protected] 

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 = [email protected] 

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

scala> Ran 

Non v'è alcuna API per la pianificazione di un compito in ritardo nella libreria standard, ma si può fare un ExecutionContext con un ritardo fisso, per utilizzare 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 = [email protected] 

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

Oppure si può usare scheduler di Akka, che è la risposta canonica a Scheduled Executor in Scala

Il vecchio one-liner:

più semplice è ancora solo future { blocking(Thread.sleep(10000L)); "done" }

ma ho voluto inserire un annuncio per questo ragazzo, che ho appena trovato, che ti dà un indicatore di progresso o un valore intermedio. Mi piacerebbe che avesse un nome diverso, tutto qui.

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] = [email protected] 

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 

Ecco una formulazione alternativa con Either, per calcolare un valore dopo un ritardo. Utilizzando Left ovviamente quando c'è ancora tempo 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]] = [email protected] 

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 
+6

non si tradurrà in migliaia di thread dormendo per 10 secondi? –

+3

Esiste una soluzione non bloccante? –

+0

@OlegMikheev Sì. –