2

I would like to do something like create two Futures, use whichever returns first in one code path and send the "slow" Future down a different code path.

A use case is, e.g. I have the option to call one of two external endpoints which are "variable speed" and "old reliable". Data from either one is fine, so I'd rather call both and use "whichever comes first" and (maybe also do something with the other output).

I can always just check whether the futures are ready, but is there something cleaner than e.g. just calling Await.ready in a loop? (And what do people call this pattern?)

cohoz
  • 750
  • 4
  • 16
  • 1
    There's https://monix.io/api/2.3/monix/eval/Task$.html#chooseFirstOf[A,B](fa:monix.eval.Task[A],fb:monix.eval.Task[B]):monix.eval.Task[Either[(A,monix.execution.CancelableFuture[B]),(monix.execution.CancelableFuture[A],B)]] – Reactormonk Feb 24 '18 at 17:28
  • You mean like `Future.firstCompletedOf(f1, f2).map(first => ...)`? http://www.scala-lang.org/files/archive/api/2.12.3/scala/concurrent/Future$.html#firstCompletedOf[T](futures:TraversableOnce[scala.concurrent.Future[T]])(implicitexecutor:scala.concurrent.ExecutionContext):scala.concurrent.Future[T] – dk14 Feb 24 '18 at 18:30
  • The cool thing about your question is that I was actually gonna close it as duplicate (the answer was extremely obvious and "no-brainer" to every Scala-developer I know), but couldn't find anything simillar (at least in 5 mins). Even [Official docs](https://docs.scala-lang.org/overviews/core/futures.html) don't mention it even once. So "+1" instead :) – dk14 Feb 24 '18 at 18:47

2 Answers2

3

This is just:

Future.firstCompletedOf(List(f1, f2)).map(first => ...) 
//or `foreach`/`Await` if you can't interact with Future-ready IO

Here are the scaladocs.

dk14
  • 22,206
  • 4
  • 51
  • 88
  • shame on me.. Used this once and forgot – Evgeny Feb 24 '18 at 18:37
  • This doesn't really allow the "separate codepath for the slow future" but it's also not clear to me that even makes sense/is useful (i.e. maybe in the use cases I care about it's OK if that thing is also done for the "first" to complete and so I can just tack it onComplete to both futures). Should have checked the scaladocs rather than just the text ones! – cohoz Feb 25 '18 at 15:11
  • @cohoz Please, try to avoid `onComplete`. If you need to wait for both futures together as well, you can use `Future.sequence` (complement of `firstCompletedOf`). Also, in FP (no side-effects) if you need data from **both** sources, you usually don't need to `firstCompletedOf`, just `sequence`. Generally, it's better to not rely on `Future`s ability to subscribe twice as it's not referentially transparent (oversimply a side-effect) too: https://stackoverflow.com/questions/44196088/why-future-has-side-effects – dk14 Feb 25 '18 at 15:28
  • @cohoz it's best to design application in a way where it takes futures as an input and returns Futures as an output (map, flatMap, sequence, firstComplete - all provided for that). The only exception is IO, and **only** IO that is NOT idempotent, i.e. (simplifying again) stateful like submitting data to DB so on. If no future-like API is provided for IO, it's best to wrap it and use separate ExecutionContext. I actually never used `onComplete` and `foreach` except for tests, I used promises few times when there was no choice. Idk why people are so eager to recommend those, it's a debug hell. – dk14 Feb 25 '18 at 15:35
  • OK. I don't need both together, and I'm happy for the function to return a future, I just also want to, e.g., log how long each external call actually took to complete for debugging purposes (both are encapsulated inside of that function, caller doesn't care). So actual application flow would just rely on `firstCompletedOf` but the logs would eventually record how long both calls took (which I thought would be an OK use of `onComplete`. Maybe that's still a bad idea but that was my original objective with this question...if so would be helpful to hear suggestions in that direction. – cohoz Feb 26 '18 at 00:43
  • @cohoz Given that it's not FP, `onComplete` is OK. However, there is Scala API for drop-wizard metrics that supports measuring Futures: https://github.com/erikvanoosten/metrics-scala/blob/master/docs/Futures.md. If you look at the sources, you might notice that they included exception handling( https://github.com/erikvanoosten/metrics-scala/blob/a97c01ef2bd5c448b095877389b5dacc31d73af5/metrics-scala/src/main/scala/nl/grons/metrics4/scala/Timer.scala) to not loose metric in case of failure of service-call itself. – dk14 Feb 26 '18 at 03:26
  • @cohoz It also could be done FP-style `def measuredCall = for{_ <- Future.unit; beg = System.currentTimeMillis; res <- call; _ = println("Time " + (System.currentTimeMillis - beg))} yield res` - the only advantage of this approach is that it would properly work with lazily evaluated Futures like `scalaz.Task`, `monix.Task` so on (as, unlike scala-library Futures, they don't eagerly start on measuredCall - we can't just `def measuredCall = {val beg = ...; ...}` ). – dk14 Feb 26 '18 at 03:34
  • 1
    If you don't care about scalaz etc: `def measuredCall = { val beg = System.currentTimeMillis; call.map{x => println("Time " + (System.currentTimeMillis - beg)); x}}`. Of course, making it completely FP would require a wrapper instead of println, like `case class Measured[T](time: Int, data: T); def measuredCall = { val beg = System.currentTimeMillis; call.map(Measured(System.currentTimeMillis - beg, _)}` – dk14 Feb 26 '18 at 03:47
1

You can create Promise and complete it from your Futures.

import scala.concurrent.{Future, Promise}
import scala.util.Random
import scala.concurrent.ExecutionContext.Implicits.global

val p = Promise[String]

Future {
  Thread.sleep(Random.nextInt(10000))
  "1"
}.onComplete(p.complete)

Future {
  Thread.sleep(Random.nextInt(10000))
  "2"
}.onComplete(p.complete)


p.future.foreach {res =>
  println(res)
}
Evgeny
  • 1,760
  • 10
  • 11
  • Given that firstCompletedOf gives a scala.concurrent.impl.Promise this is helpful to see. – cohoz Feb 25 '18 at 15:13