kolosy
kolosy

Reputation: 3099

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 DeferredResolver in sangria-graphql + akka. I've stolen their demo code, which looks like this

  Future.fromTry(Try(
    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:

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

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

The pertinent piece here is that I pick the first element in the list, send it to an ActorRef 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 Future

  Future.fromTry(Try(
    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 Account (the return type of function marshallAccount, it's of type Promise. If I understand correctly, it's because instead of having a return type of Future[Account], this has a type of Future[Future[Account]]

How do I flatten this?

Upvotes: 0

Views: 165

Answers (1)

flavian
flavian

Reputation: 28511

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.

Upvotes: 3

Related Questions