Related
As mentioned in the jump-start guide, mapN will run all the futures in parallel, so I created the below simple Scala program, but a sample run shows diff to be 9187 ms and diffN to be 9106 ms. So it looks like that the mapN is also running the futures sequentially, isn't it? Please let me know if I am missing something?
package example
import scala.concurrent.Future
import scala.concurrent.ExecutionContext.Implicits.global
import java.time.LocalDateTime
import java.time.Duration
import scala.util.Failure
import scala.util.Success
import java.time.ZoneOffset
import cats.instances.future._
import cats.syntax.apply._
object FutureEx extends App {
val before = LocalDateTime.now()
val sum = for {
a <- getA
b <- getB
c <- getC
} yield (a + b + c)
sum onComplete {
case Failure(ex) => println(s"Error: ${ex.getMessage()}")
case Success(value) =>
val after = LocalDateTime.now()
println(s"before: $before")
println(s"after: $after")
val diff = getDiff(before, after)
println(s"diff: $diff")
println(s"sum: $value")
}
// let the above finish
Thread.sleep(20000)
val beforeN = LocalDateTime.now()
val usingMapN = (getA, getB, getC).mapN(add)
usingMapN onComplete {
case Failure(ex) => println(s"Error: ${ex.getMessage()}")
case Success(value) =>
val afterN = LocalDateTime.now()
println(s"beforeN: $beforeN")
println(s"afterN: $afterN")
val diff = getDiff(beforeN, afterN)
println(s"diffN: $diff")
println(s"sum: $value")
}
def getA: Future[Int] = {
println("inside A")
Thread.sleep(3000)
Future.successful(2)
}
def getB: Future[Int] = {
println("inside B")
Thread.sleep(3000)
Future.successful(3)
}
def getC: Future[Int] = {
println("inside C")
Thread.sleep(3000)
Future.successful(4)
}
def add(a: Int, b: Int, c: Int) = a + b + c
def getDiff(before: LocalDateTime, after: LocalDateTime): Long = {
Duration.between(before.toInstant(ZoneOffset.UTC), after.toInstant(ZoneOffset.UTC)).toMillis()
}
}
Because you have sleep outside Future it should be like:
def getA: Future[Int] = Future {
println("inside A")
Thread.sleep(3000)
2
}
So you start async Future with apply - Future.successful on the other hand returns pure value, meaning you execute sleep in same thread.
The time is going before mapN is ever called.
(getA, getB, getC).mapN(add)
This expression is creating a tuple (sequentially) and then calling mapN on it. So it is calling getA then getB then getC and since each of them has a 3 second delay, it takes 9 seconds.
The objective of this code is to take a sequence of futures, process them with Future.sequence, generate another sequence of futures and process them again with another Future.sequence.
The problem is that it doesn't print anything. What's wrong with this code?
object TestFutures extends App {
def future (i:Int) = Future { i }
def future2 (i:Int) = Future { i * 20 }
val futureResult = (1 to 10).map {
x => future(x)
}
var futureInts = Seq[Future[Int]]()
Future.sequence(futureResult).map{ list =>
list.foreach( y => futureInts = futureInts :+ future2(y))
}
Future.sequence(futureInts).map { list2 =>
list2.foreach( z => println(z))
}
Thread.sleep(5000)
}
It does work, you just have a race condition between the Thread.sleep and the futures finishing execution. You can wait on the futures completion with Await.result:
import scala.concurrent.{Await, Future}
import scala.concurrent.ExecutionContext.Implicits._
import scala.concurrent.duration._
def future(i: Int) = Future.successful(i)
def future2(i: Int) = Future.successful(i * 20)
val futureResult = (1 to 10).map(x => future(x))
val firstResult =
Future
.sequence(futureResult)
.flatMap(list => Future.sequence(list.map(future2))
val sequence = Await.result(firstResult, 10 seconds)
sequence.foreach(println)
Note you should not be synchronously blocking on futures in production code, this is merely for the demonstration that the future does do what you want it to.
Suppose I need to run two concurrent computations, wait for both of them, and then combine their results. More specifically, I need to run f1: X1 => Y1 and f2: X2 => Y2 concurrently and then call f: (Y1, Y2) => Y to finally get a value of Y.
I can create future computations fut1: X1 => Future[Y1] and fut2: X2 => Future[Y2] and then compose them to get fut: (X1, X2) => Future[Y] using monadic composition.
The problem is that monadic composition implies sequential wait. In our case it implies that we wait for one future first and then we will wait for another. For instance. if it takes 2 sec. to the first future to complete and just 1 sec. to the 2nd future to fail we waste 1 sec.
Thus it looks like we need an applicative composition of the futures to wait till either both complete or at least one future fails. Does it make sense ? How would you implement <*> for futures ?
None of the methods in other answers does the right thing in case of a future that fails quickly plus a future that succeeds after a long time.
But such a method can be implemented manually:
def smartSequence[A](futures: Seq[Future[A]]): Future[Seq[A]] = {
val counter = new AtomicInteger(futures.size)
val result = Promise[Seq[A]]()
def attemptComplete(t: Try[A]): Unit = {
val remaining = counter.decrementAndGet
t match {
// If one future fails, fail the result immediately
case Failure(cause) => result tryFailure cause
// If all futures have succeeded, complete successful result
case Success(_) if remaining == 0 =>
result tryCompleteWith Future.sequence(futures)
case _ =>
}
}
futures.foreach(_ onComplete attemptComplete)
result.future
}
ScalaZ does a similar thing internally, so both f1 |#| f2 and List(f1, f2).sequence fail immediately after any of the futures fails.
Here is a quick test of the failing time for those methods:
import java.util.Date
import scala.concurrent.Future
import scala.concurrent.ExecutionContext.Implicits.global
import scalaz._, Scalaz._
object ReflectionTest extends App {
def f1: Future[Unit] = Future {
Thread.sleep(2000)
}
def f2: Future[Unit] = Future {
Thread.sleep(1000)
throw new RuntimeException("Failure")
}
def test(name: String)(
f: (Future[Unit], Future[Unit]) => Future[Unit]
): Unit = {
val start = new Date().getTime
f(f1, f2).andThen {
case _ =>
println(s"Test $name completed in ${new Date().getTime - start}")
}
Thread.sleep(2200)
}
test("monadic") { (f1, f2) => for (v1 <- f1; v2 <- f2) yield () }
test("zip") { (f1, f2) => (f1 zip f2).map(_ => ()) }
test("Future.sequence") {
(f1, f2) => Future.sequence(Seq(f1, f2)).map(_ => ())
}
test("smartSequence") { (f1, f2) => smartSequence(Seq(f1, f2)).map(_ => ())}
test("scalaz |#|") { (f1, f2) => (f1 |#| f2) { case _ => ()}}
test("scalaz sequence") { (f1, f2) => List(f1, f2).sequence.map(_ => ())}
Thread.sleep(30000)
}
And the result on my machine is:
Test monadic completed in 2281
Test zip completed in 2008
Test Future.sequence completed in 2007
Test smartSequence completed in 1005
Test scalaz |#| completed in 1003
Test scalaz sequence completed in 1005
The problem is that monadic composition implies sequential wait. In our case it implies that we wait for one future first and then we will wait for another.
This is unfortunately true.
import java.util.Date
import scala.concurrent.Future
import scala.concurrent.ExecutionContext.Implicits.global
object Test extends App {
def timestamp(label: String): Unit = Console.println(label + ": " + new Date().getTime.toString)
timestamp("Start")
for {
step1 <- Future {
Thread.sleep(2000)
timestamp("step1")
}
step2 <- Future {
Thread.sleep(1000)
timestamp("step2")
}
} yield { timestamp("Done") }
Thread.sleep(4000)
}
Running this code outputs:
Start: 1430473518753
step1: 1430473520778
step2: 1430473521780
Done: 1430473521781
Thus it looks like we need an applicative composition of the futures to wait till either both complete or at least one future fails.
I am not sure applicative composition has anything to do with the concurrent strategy. Using for comprehensions, you get a result if all futures complete or a failure if any of them fails. So it's semantically the same.
Why Are They Running Sequentially
I think the reason why futures are run sequentially is because step1 is available within step2 (and in the rest of the computation). Essentially we can convert the for block as:
def step1() = Future {
Thread.sleep(2000)
timestamp("step1")
}
def step2() = Future {
Thread.sleep(1000)
timestamp("step2")
}
def finalStep() = timestamp("Done")
step1().flatMap(step1 => step2()).map(finalStep())
So the result of previous computations are available to the rest of the steps. It differs from <?> & <*> in this respect.
How To Run Futures In Parallel
#andrey-tyukin's code runs futures in parallel:
import java.util.Date
import scala.concurrent.Future
import scala.concurrent.ExecutionContext.Implicits.global
object Test extends App {
def timestamp(label: String): Unit = Console.println(label + ": " + new Date().getTime.toString)
timestamp("Start")
(Future {
Thread.sleep(2000)
timestamp("step1")
} zip Future {
Thread.sleep(1000)
timestamp("step2")
}).map(_ => timestamp("Done"))
Thread.sleep(4000)
}
Output:
Start: 1430474667418
step2: 1430474668444
step1: 1430474669444
Done: 1430474669446
Your post seems to contain two more or less independent questions.
I will address the concrete practical problem of running two concurrent computations first. The question about Applicative is answered in the very end.
Suppose you have two asynchronous functions:
val f1: X1 => Future[Y1]
val f2: X2 => Future[Y2]
And two values:
val x1: X1
val x2: X2
Now you can start the computations in multiple different ways. Let's take a look at some of them.
Starting computations outside of for (parallel)
Suppose you do this:
val y1: Future[Y1] = f1(x1)
val y2: Future[Y2] = f2(x2)
Now, the computations f1 and f2 are already running. It does not matter in which order you collect the results. You could do it with a for-comprehension:
val y: Future[(Y1,Y2)] = for(res1 <- y1; res2 <- y2) yield (res1,res2)
Using the expressions y1 and y2 in the for-comprehension does not interfere with the order of computation of y1 and y2, they are still being computed in parallel.
Starting computations inside of for (sequential)
If we simply take the definitions of y1 and y2, and plug them into the for comprehension directly, we will still get the same result, but the order of execution will be different:
val y = for (res1 <- f1(x1); res2 <- f2(x2)) yield (res1, res2)
translates into
val y = f1(x1).flatMap{ res1 => f2(x2).map{ res2 => (res1, res2) } }
in particular, the second computation starts after the first one has terminated. This is usually not what one wants to have.
Here, a basic substitution principle is violated. If there were no side-effects, one probably could transform this version into the previous one, but in Scala, one has to take care of the order of execution explicitly.
Zipping futures (parallel)
Futures respect products. There is a method Future.zip, which allows you to do this:
val y = f1(x1) zip f2(x2)
This would run both computations in parallel until both are done, or until one of them fails.
Demo
Here is a little script that demonstrates this behaviour (inspired by muhuk's post):
import scala.concurrent._
import scala.concurrent.duration._
import scala.concurrent.ExecutionContext.Implicits.global
import java.lang.Thread.sleep
import java.lang.System.{currentTimeMillis => millis}
var time: Long = 0
val x1 = 1
val x2 = 2
// this function just waits
val f1: Int => Future[Unit] = {
x => Future { sleep(x * 1000) }
}
// this function waits and then prints
// elapsed time
val f2: Int => Future[Unit] = {
x => Future {
sleep(x * 1000)
val elapsed = millis() - time
printf("Time: %1.3f seconds\n", elapsed / 1000.0)
}
}
/* Outside `for` */ {
time = millis()
val y1 = f1(x1)
val y2 = f2(x2)
val y = for(res1 <- y1; res2 <- y2) yield (res1,res2)
Await.result(y, Duration.Inf)
}
/* Inside `for` */ {
time = millis()
val y = for(res1 <- f1(x1); res2 <- f2(x2)) yield (res1, res2)
Await.result(y, Duration.Inf)
}
/* Zip */ {
time = millis()
val y = f1(x1) zip f2(x2)
Await.result(y, Duration.Inf)
}
Output:
Time: 2.028 seconds
Time: 3.001 seconds
Time: 2.001 seconds
Applicative
Using this definition from your other post:
trait Applicative[F[_]] {
def apply[A, B](f: F[A => B]): F[A] => F[B]
}
one could do something like this:
object FutureApplicative extends Applicative[Future] {
def apply[A, B](ff: Future[A => B]): Future[A] => Future[B] = {
fa => for ((f,a) <- ff zip fa) yield f(a)
}
}
However, I'm not sure what this has to do with your concrete problem, or with understandable and readable code. A Future already is a monad (this is stronger than Applicative), and there is even built-in syntax for it, so I don't see any advantages in adding some Applicatives here.
It needs not be sequential. The future computation may start the moment the future is created. Of course, if the future is created by the flatMap argument (and it will necessary be so if it needs the result of the first computation), then it will be sequential. But in code such as
val f1 = Future {....}
val f2 = Future {....}
for (a1 <- f1; a2 <- f2) yield f(a1, a2)
you get concurrent execution.
So the implementation of Applicative implied by Monad is ok.
I have following Scala code, used with Scala 2.10
package reactive
import scala.concurrent.future
import scala.concurrent.ExecutionContext.Implicits.global
import scala.concurrent.Future
object futuresum extends App {
def f1 = future { 12 }
def f2 = future { 13 }
val result1 = f1.flatMap(x => f2.map(y => x + y))
println(result1.value)
val result2 = for (x <- f1; y <- f2) yield (x + y)
println(result2.value)
}
This code should print 25, but it prints None:
None
None
What is wrong with this code?
EDIT
This works for me now:
package reactive
import scala.concurrent.future
import scala.concurrent.ExecutionContext.Implicits.global
import scala.concurrent.Future
import scala.concurrent.Await
import scala.concurrent.duration.Duration
object futuresum extends App {
def f1 = future { 12 }
def f2 = future { 13 }
val result1 = f1.flatMap(x => f2.map(y => x + y))
result1.onComplete(b => println(b))
val result2 = for (x <- f1; y <- f2) yield (x + y)
result2.onComplete(b => println(b))
Await.result(result1, Duration.Inf)
Await.result(result2, Duration.Inf)
}
My guess is that between the "result"s and its println the futures haven't had enough time to spawn/send-to their threads. Check this quick REPL session:
scala> f1.flatMap(x => f2.map(y => x + y)).value
res2: Option[scala.util.Try[Int]] = None
scala> f1.flatMap(x => f2.map(y => x + y))
res3: scala.concurrent.Future[Int] = scala.concurrent.impl.Promise$DefaultPromise#1c9dac55
scala> res3.value
res4: Option[scala.util.Try[Int]] = Some(Success(25))
Let's try something different:
scala> f1.flatMap(x => f2.map(y => x + y)).onComplete(println(_))
scala> Success(25) // Press enter here. onComplete mixed with REPL output.
Another try:
scala> val later=f1.flatMap(x => f2.map(y => x + y)); later.onComplete(println(_)); println(later.value);
None
later: scala.concurrent.Future[Int] = scala.concurrent.impl.Promise$DefaultPromise#7019b737
Success(25)
Now that was a confusing output. It's a good illustration of my point, though:
First, create later which contains a computation that will be
executed sometime in future.
Bind this computation with a event that will be triggered when it completes.
Print the result of the possibly unfinished computation.
Now think in two lines of execution. 1, 2 and 3 happens in the first, main line of execution, or the main thread. What lies inside the future, { 12 }, map, flatMat happens in another line of execution.
I'll steal RĂ©gis Jean-Gilles' excellent answer from https://stackoverflow.com/a/16358119/3328933
"Futures are executed on a dedicated thread pool. If your main program does not wait for the future, it will exit immediately and the future won't have a chance to execute. What you can do here is to use Await in your main program to block the main thread until the future executes:"
package reactive
import scala.concurrent.Await
import scala.concurrent.ExecutionContext.Implicits.global
import scala.concurrent.future
import scala.concurrent.duration._
object futuresum extends App {
def f1 = future { 12 }
def f2 = future { 13 }
val result1 = f1.flatMap(x => f2.map(y => x + y))
result1 onSuccess { case res => println(res) }
Await.result(result1, 2 seconds)
val result2 = for (x <- f1; y <- f2) yield (x + y)
result2 onSuccess { case res => println(res) }
Await.result(result2, 2 seconds)
}
Use Await.result(result, duration) or Thread.sleep to wait future thread finish.
Then you can check future.isComplete to make sure that future is finished.
Await.result(result2, 3 seconds)
if(result2.isCompleted)
println(result2.value)
Suppose I have several futures and need to wait until either any of them fails or all of them succeed.
For example: Let there are 3 futures: f1, f2, f3.
If f1 succeeds and f2 fails I do not wait for f3 (and return failure to the client).
If f2 fails while f1 and f3 are still running I do not wait for them (and return failure)
If f1 succeeds and then f2 succeeds I continue waiting for f3.
How would you implement it?
You could use a for-comprehension as follows instead:
val fut1 = Future{...}
val fut2 = Future{...}
val fut3 = Future{...}
val aggFut = for{
f1Result <- fut1
f2Result <- fut2
f3Result <- fut3
} yield (f1Result, f2Result, f3Result)
In this example, futures 1, 2 and 3 are kicked off in parallel. Then, in the for comprehension, we wait until the results 1 and then 2 and then 3 are available. If either 1 or 2 fails, we will not wait for 3 anymore. If all 3 succeed, then the aggFut val will hold a tuple with 3 slots, corresponding to the results of the 3 futures.
Now if you need the behavior where you want to stop waiting if say fut2 fails first, things get a little trickier. In the above example, you would have to wait for fut1 to complete before realizing fut2 failed. To solve that, you could try something like this:
val fut1 = Future{Thread.sleep(3000);1}
val fut2 = Promise.failed(new RuntimeException("boo")).future
val fut3 = Future{Thread.sleep(1000);3}
def processFutures(futures:Map[Int,Future[Int]], values:List[Any], prom:Promise[List[Any]]):Future[List[Any]] = {
val fut = if (futures.size == 1) futures.head._2
else Future.firstCompletedOf(futures.values)
fut onComplete{
case Success(value) if (futures.size == 1)=>
prom.success(value :: values)
case Success(value) =>
processFutures(futures - value, value :: values, prom)
case Failure(ex) => prom.failure(ex)
}
prom.future
}
val aggFut = processFutures(Map(1 -> fut1, 2 -> fut2, 3 -> fut3), List(), Promise[List[Any]]())
aggFut onComplete{
case value => println(value)
}
Now this works correctly, but the issue comes from knowing which Future to remove from the Map when one has been successfully completed. As long as you have some way to properly correlate a result with the Future that spawned that result, then something like this works. It just recursively keeps removing completed Futures from the Map and then calling Future.firstCompletedOf on the remaining Futures until there are none left, collecting the results along the way. It's not pretty, but if you really need the behavior you are talking about, then this, or something similar could work.
You can use a promise, and send to it either the first failure, or the final completed aggregated success:
def sequenceOrBailOut[A, M[_] <: TraversableOnce[_]](in: M[Future[A]] with TraversableOnce[Future[A]])(implicit cbf: CanBuildFrom[M[Future[A]], A, M[A]], executor: ExecutionContext): Future[M[A]] = {
val p = Promise[M[A]]()
// the first Future to fail completes the promise
in.foreach(_.onFailure{case i => p.tryFailure(i)})
// if the whole sequence succeeds (i.e. no failures)
// then the promise is completed with the aggregated success
Future.sequence(in).foreach(p trySuccess _)
p.future
}
Then you can Await on that resulting Future if you want to block, or just map it into something else.
The difference with for comprehension is that here you get the error of the first to fail, whereas with for comprehension you get the first error in traversal order of the input collection (even if another one failed first). For example:
val f1 = Future { Thread.sleep(1000) ; 5 / 0 }
val f2 = Future { 5 }
val f3 = Future { None.get }
Future.sequence(List(f1,f2,f3)).onFailure{case i => println(i)}
// this waits one second, then prints "java.lang.ArithmeticException: / by zero"
// the first to fail in traversal order
And:
val f1 = Future { Thread.sleep(1000) ; 5 / 0 }
val f2 = Future { 5 }
val f3 = Future { None.get }
sequenceOrBailOut(List(f1,f2,f3)).onFailure{case i => println(i)}
// this immediately prints "java.util.NoSuchElementException: None.get"
// the 'actual' first to fail (usually...)
// and it returns early (it does not wait 1 sec)
Here is a solution without using actors.
import scala.util._
import scala.concurrent._
import java.util.concurrent.atomic.AtomicInteger
// Nondeterministic.
// If any failure, return it immediately, else return the final success.
def allSucceed[T](fs: Future[T]*): Future[T] = {
val remaining = new AtomicInteger(fs.length)
val p = promise[T]
fs foreach {
_ onComplete {
case s # Success(_) => {
if (remaining.decrementAndGet() == 0) {
// Arbitrarily return the final success
p tryComplete s
}
}
case f # Failure(_) => {
p tryComplete f
}
}
}
p.future
}
You can do this with futures alone. Here's one implementation. Note that it won't terminate execution early! In that case you need to do something more sophisticated (and probably implement the interruption yourself). But if you just don't want to keep waiting for something that isn't going to work, the key is to keep waiting for the first thing to finish, and stop when either nothing is left or you hit an exception:
import scala.annotation.tailrec
import scala.util.{Try, Success, Failure}
import scala.concurrent._
import scala.concurrent.duration.Duration
import ExecutionContext.Implicits.global
#tailrec def awaitSuccess[A](fs: Seq[Future[A]], done: Seq[A] = Seq()):
Either[Throwable, Seq[A]] = {
val first = Future.firstCompletedOf(fs)
Await.ready(first, Duration.Inf).value match {
case None => awaitSuccess(fs, done) // Shouldn't happen!
case Some(Failure(e)) => Left(e)
case Some(Success(_)) =>
val (complete, running) = fs.partition(_.isCompleted)
val answers = complete.flatMap(_.value)
answers.find(_.isFailure) match {
case Some(Failure(e)) => Left(e)
case _ =>
if (running.length > 0) awaitSuccess(running, answers.map(_.get) ++: done)
else Right( answers.map(_.get) ++: done )
}
}
}
Here's an example of it in action when everything works okay:
scala> awaitSuccess(Seq(Future{ println("Hi!") },
Future{ Thread.sleep(1000); println("Fancy meeting you here!") },
Future{ Thread.sleep(2000); println("Bye!") }
))
Hi!
Fancy meeting you here!
Bye!
res1: Either[Throwable,Seq[Unit]] = Right(List((), (), ()))
But when something goes wrong:
scala> awaitSuccess(Seq(Future{ println("Hi!") },
Future{ Thread.sleep(1000); throw new Exception("boo"); () },
Future{ Thread.sleep(2000); println("Bye!") }
))
Hi!
res2: Either[Throwable,Seq[Unit]] = Left(java.lang.Exception: boo)
scala> Bye!
For this purpose I would use an Akka actor. Unlike the for-comprehension, it fails as soon as any of the futures fail, so it's a bit more efficient in that sense.
class ResultCombiner(futs: Future[_]*) extends Actor {
var origSender: ActorRef = null
var futsRemaining: Set[Future[_]] = futs.toSet
override def receive = {
case () =>
origSender = sender
for(f <- futs)
f.onComplete(result => self ! if(result.isSuccess) f else false)
case false =>
origSender ! SomethingFailed
case f: Future[_] =>
futsRemaining -= f
if(futsRemaining.isEmpty) origSender ! EverythingSucceeded
}
}
sealed trait Result
case object SomethingFailed extends Result
case object EverythingSucceeded extends Result
Then, create the actor, send a message to it (so that it will know where to send its reply to) and wait for a reply.
val actor = actorSystem.actorOf(Props(new ResultCombiner(f1, f2, f3)))
try {
val f4: Future[Result] = actor ? ()
implicit val timeout = new Timeout(30 seconds) // or whatever
Await.result(f4, timeout.duration).asInstanceOf[Result] match {
case SomethingFailed => println("Oh noes!")
case EverythingSucceeded => println("It all worked!")
}
} finally {
// Avoid memory leaks: destroy the actor
actor ! PoisonPill
}
This question has been answered but I am posting my value class solution (value classes were added in 2.10) since there isn't one here. Please feel free to criticize.
implicit class Sugar_PimpMyFuture[T](val self: Future[T]) extends AnyVal {
def concurrently = ConcurrentFuture(self)
}
case class ConcurrentFuture[A](future: Future[A]) extends AnyVal {
def map[B](f: Future[A] => Future[B]) : ConcurrentFuture[B] = ConcurrentFuture(f(future))
def flatMap[B](f: Future[A] => ConcurrentFuture[B]) : ConcurrentFuture[B] = concurrentFutureFlatMap(this, f) // work around no nested class in value class
}
def concurrentFutureFlatMap[A,B](outer: ConcurrentFuture[A], f: Future[A] => ConcurrentFuture[B]) : ConcurrentFuture[B] = {
val p = Promise[B]()
val inner = f(outer.future)
inner.future onFailure { case t => p.tryFailure(t) }
outer.future onFailure { case t => p.tryFailure(t) }
inner.future onSuccess { case b => p.trySuccess(b) }
ConcurrentFuture(p.future)
}
ConcurrentFuture is a no overhead Future wrapper that changes the default Future map/flatMap from do-this-then-that to combine-all-and-fail-if-any-fail. Usage:
def func1 : Future[Int] = Future { println("f1!");throw new RuntimeException; 1 }
def func2 : Future[String] = Future { Thread.sleep(2000);println("f2!");"f2" }
def func3 : Future[Double] = Future { Thread.sleep(2000);println("f3!");42.0 }
val f : Future[(Int,String,Double)] = {
for {
f1 <- func1.concurrently
f2 <- func2.concurrently
f3 <- func3.concurrently
} yield for {
v1 <- f1
v2 <- f2
v3 <- f3
} yield (v1,v2,v3)
}.future
f.onFailure { case t => println("future failed $t") }
In the example above, f1,f2 and f3 will run concurrently and if any fail in any order the future of the tuple will fail immediately.
You might want to checkout Twitter's Future API. Notably the Future.collect method. It does exactly what you want: https://twitter.github.io/scala_school/finagle.html
The source code Future.scala is available here:
https://github.com/twitter/util/blob/master/util-core/src/main/scala/com/twitter/util/Future.scala
You can use this:
val l = List(1, 6, 8)
val f = l.map{
i => future {
println("future " +i)
Thread.sleep(i* 1000)
if (i == 12)
throw new Exception("6 is not legal.")
i
}
}
val f1 = Future.sequence(f)
f1 onSuccess{
case l => {
logInfo("onSuccess")
l.foreach(i => {
logInfo("h : " + i)
})
}
}
f1 onFailure{
case l => {
logInfo("onFailure")
}