kolosy kolosy - 1 year ago 80
Scala Question

Getting lost in Scala Futures

I'm slowly wrapping my brain around Futures in Scala, and have a bit of a layer cake going on that I'm trying to unravel.

The specific use case is a

in sangria-graphql + akka. I've stolen their demo code, which looks like this

friendIds map (id => CharacterRepo.humans.find(_.id == id) orElse CharacterRepo.droids.find(_.id == id))))

and added my own modification to it. Theirs does an in-memory lookup, whereas mine asks something of another actor:

accountIds match {
case h :: _ =>
val f = sender ? TargetedMessage(h)
val resp = Await.result(f, timeout.duration).asInstanceOf[TargetedMessage]

case _ => throw new Exception("Not found")

The pertinent piece here is that I pick the first element in the list, send it to an
that I got elsewhere and wait for the result. This works. What I'd like to do, however, is not have to wait for the result here, but return the whole thing as a

accountIds match {
case h :: _ =>
sender ? TargetedMessage(h) map {
case resp:TargetedMessage => marshallAccount(resp.body)

case _ => throw new Exception("Not found")

This doesn't work. When this is consumed, instead of being of type
(the return type of function
, it's of type Promise. If I understand correctly, it's because instead of having a return type of
, this has a type of

How do I flatten this?

Answer Source

You are looking at the wrong API method. Future.fromTry is used to create an immediately resolved Future, meaning the call is not actually asynchronous. Dive into the implementation of Future.fromTry which will take you to:

def fromTry[T](result: Try[T]): Promise[T] = new impl.Promise.KeptPromise[T](result)

A promise kept is basically something that has already happened, so just like Future.successful this is just used to ensure the right return type or similar, it's not actually a way to make something async.

The reason why the return type is Future[Future[Something]] is because you are trying to wrap something that already returns a future into another future.

The ask pattern, namely sender ? TargetMessage(h) is a way to ask something of an actor and await for a result, which will return a future.

The correct way to approach this:

val future: Future[Account] = accountIds match {
  case h :: _ => sender ? TargetedMessage(h) map (marshallAccount(_.body)
  case _ => Future.failed(throw new Exception("Not found"))

Basically you need to use Future.failed to return a failed future from an exception if you want to keep the return type consistent. It's worth reviewing this tutorial to learn a bit more about Futures and how to write application logic with them.

Recommended from our users: Dynamic Network Monitoring from WhatsUp Gold from IPSwitch. Free Download