How to call a method over and over until it returns a `Future` containing` None`
For a method that returns Future
like this ...
def remove(id: String): Future[Option[User]] = Future {
// removes and returns the user identified by `id`
}
... how can I call it over and over until it returns the Future
containing value None
?
EDIT
It might be worth mentioning that I don't need to collect results. I just need to call the method while it finds the user to delete. The idea would be to loop
stop when it remove
returns Future[None]
.
source to share
Someone commented earlier that it doesn't make sense.
The surprise for me was that there is no easy to lazy consumption of futures. Future.find
is like firstCompletedOf
and doesn't mean find first in traversable order
.
scala> import concurrent._, ExecutionContext.Implicits._
import concurrent._
import ExecutionContext.Implicits._
scala> import java.util.concurrent.atomic._
import java.util.concurrent.atomic._
scala> val count = new AtomicInteger(10)
count: java.util.concurrent.atomic.AtomicInteger = 10
scala> def f(s: String) = Future { if (count.decrementAndGet <= 0) None else Some(s) }
f: (s: String)scala.concurrent.Future[Option[String]]
scala> def g(ss: List[String]): Future[List[String]] = f("hello") flatMap { case None => Future.successful(ss) case Some(s) => g(s :: ss) }
g: (ss: List[String])scala.concurrent.Future[List[String]]
scala> g(Nil)
res0: scala.concurrent.Future[List[String]] = scala.concurrent.impl.Promise$DefaultPromise@65a15628
scala> .value
res1: Option[scala.util.Try[List[String]]] = Some(Success(List(hello, hello, hello, hello, hello, hello, hello, hello, hello)))
To illustrate the usefulness of not blocking:
scala> :pa
// Entering paste mode (ctrl-D to finish)
import scala.util._
import concurrent._, ExecutionContext.Implicits._
import java.util.concurrent.atomic._
class Work {
val count = new AtomicInteger(10)
def f(s: String) = Future {
if (count.decrementAndGet <= 0) None else Some(s)
} andThen {
case Success(Some(x)) => Console println s"Calculated $x"
case Success(None) => Console println "Done."
case _ => Console println "Failed."
}
}
// Exiting paste mode, now interpreting.
import scala.util._
import concurrent._
import ExecutionContext.Implicits._
import java.util.concurrent.atomic._
defined class Work
Shows the version Stream
that will not evaluate the prefix until the consumption thread goes through the lock. Expects:
scala> val work = new Work
work: Work = Work@1b45c0e
scala> Stream continually work.f("hello") takeWhile { x => Await.result(x, duration.Duration.Inf).nonEmpty }
Calculated hello
res0: scala.collection.immutable.Stream[scala.concurrent.Future[Option[String]]] = Stream(scala.concurrent.impl.Promise$DefaultPromise@66629f63, ?)
scala> .toList
Calculated hello
Calculated hello
Calculated hello
Calculated hello
Calculated hello
Calculated hello
Calculated hello
Calculated hello
Done.
res1: List[scala.concurrent.Future[Option[String]]] = List(scala.concurrent.impl.Promise$DefaultPromise@66629f63, scala.concurrent.impl.Promise$DefaultPromise@610db97e, scala.concurrent.impl.Promise$DefaultPromise@6f0628de, scala.concurrent.impl.Promise$DefaultPromise@3fabf088, scala.concurrent.impl.Promise$DefaultPromise@1e392345, scala.concurrent.impl.Promise$DefaultPromise@12f3afb5, scala.concurrent.impl.Promise$DefaultPromise@4ced35ed, scala.concurrent.impl.Promise$DefaultPromise@2c22a348, scala.concurrent.impl.Promise$DefaultPromise@7bd69e82)
scala> .foreach (Console println _.value.get)
Success(Some(hello))
Success(Some(hello))
[snip]
A different behavior is perhaps more desirable when you get a future that contains the result of evaluating a prefix:
scala> :pa
// Entering paste mode (ctrl-D to finish)
val work = new Work
def g(ss: List[String]): Future[List[String]] = work.f("hello") flatMap {
case None => Future.successful(ss)
case Some(s) => g(s :: ss)
}
// Exiting paste mode, now interpreting.
work: Work = Work@796d3c9f
g: (ss: List[String])scala.concurrent.Future[List[String]]
scala> g(Nil)
Calculated hello
Calculated hello
res3: scala.concurrent.Future[List[String]] = scala.concurrent.impl.Promise$DefaultPromise@99a78d7
Calculated hello
Calculated hello
Calculated hello
scala> Calculated hello
Calculated hello
Calculated hello
Calculated hello
Done.
Use the future:
scala> .value
res5: Option[scala.util.Try[List[String]]] = Some(Success(List(hello, hello, hello, hello, hello, hello, hello, hello, hello)))
source to share
Stream#continually
to do the same endlessly, and Stream#takeWhile
to stop it at a certain point.
http://www.scala-lang.org/api/2.11.0/index.html#scala.collection.immutable.Stream
Stream.continually(/*remove*/).takeWhile(/*not Future[None]*/)
source to share
Here he is:
import concurrent._, ExecutionContext.Implicits._
import java.util.concurrent.atomic._
val count = new AtomicInteger(10)
def f(s: String) = Future {
if (count.decrementAndGet <= 0) None else Some(s)
}
Iterator continually {
f("hello")
} takeWhile {
Await.result(_, duration.Duration.Inf).nonEmpty
} foreach { _.map { _.map {
println
}}
Hope this helps.
source to share