Blankman
Blankman

Reputation: 267150

How to traverse a Set[Future[Option[User]]] and mutate a map

I have a mutable map that contains users:

val userMap = mutable.Map.empty[Int, User] // Int is user.Id

Now I need to load the new users, and add them to the map. I have the following api methods:

def getNewUsers(): Seq[Int]
def getUser(userId: Int): Future[Option[User]]

So I first get all the users I need to load:

val newUserIds: Set[Int] = api.getNewUsers

I now need to load each user, but not sure how to do getUser returns a Future[Option[User]].

I tried this:

api.getNewUsers().map( getUser(_) )

But that returns a Set[Future[Option[User]]]

I'm not sure how to use Set[Future[Option[User]]] to update my userMap now.

Upvotes: 1

Views: 238

Answers (2)

Tyler
Tyler

Reputation: 18177

You'll have to wait for all of the Futures to finish. You can use Future.sequence to transform your Set[Future[_]] into a Future[Set], so you can wait for them all to finish:

val s: Set[scala.concurrent.Future[Some[User]]] = Set(Future(Some(User(1))), Future(Some(User(2))))

val f: Future[Set[Some[User]]] = Future.sequence(s)

f.map(users => users.foreach(u => /* your code here */))

However, using a mutable Map may be dangerous because it's possible to open yourself up to race conditions. Futures are executed in different threads, and if you altering a mutable object's state in different threads, bad things will happen.

Upvotes: 2

Carlos Caldas
Carlos Caldas

Reputation: 816

You can use Future.sequence:

transforms a TraversableOnce[Future[A]] into a Future[TraversableOnce[A]]. Useful for reducing many Futures into a single Future from Scala Future

You can try:

val result : Future[Seq[Option[User]]] = 
  Future.sequence(
      api.getNewUsers().map( getUser )
  )
result.andThen {
    case Success(users) => 
        users.flatten.foreach(u => yourMap += u.id -> u)
}

Upvotes: 1

Related Questions