18

I'm aware that my problem might seem a little bit complex. But I'll try to express myself well.

I have this method which I want to return a Map[String, List[String]] filled with data.

def myFunction():Map[String, List[String]] = {

  val userMap = Map[String, String](("123456", "ASDBYYBAYGS456789"),
                                    ("54321", "HGFDSA5432"))

  //the result map to return when all data is collected and added
  val resultMap:Future[Map[String, List[String]]]

  //when this map is finished (filled) this map is set to resultMap
  val progressMap = Map[String, List[String]]()

  for(user <- userMap){

     //facebook graph API call to get posts.
     val responsePost = WS.url("async get to facebook url").get()

     responsePosts.flatMap { response => 
        val jsonBody = response.json
        val dataList = List[String]()

        for(i <-0 until 5){

           //parse the json-data to strings
           val messages = (jsonBody.\("statuses").\("data")(i).\("message"))
           val likesArray = (jsonBody.\("statuses").\("data")(i).\\("data")).flatMap(_.as[List[JsObject]])
           val likes = likesArray.length

           //Put post with likes in temporary list
           dataList ::=  ("Post: " + message.toString + " Likes: " + likes.toString)
        }  

           //facebook graph API call to get friends.
           val responseFriends = WS.url("async get to facebook url").get()

           responseFriends.map { response =>
               val jsonBody = response.json
               val friendCount = jsonBody.\("data")(0).\("friend_count").toString

               //add "Friends: xxx" to the dataList and add the new row to resultMap containig a list with post and friends.
               dataList ::= ("Friends: " + friendCount)
               progressMap += user._1 -> dataList

               //check if all users has been updated
               if(progressMap.size == userMap.size){
                  resultMap = progressMap
               }
            }
       }
    }

    //return the resultMap.
    return resultMap
 }
}

My code might not be written with optimal syntax.

But what I want is to return this resultMap with data. My problem is that since the "get to facebook url" is done asynchronously this resultMap is returned empty. I do not want this to be empty ofcourse.

This code in my method is my solution so far. It does not work, obviously, but I hope you can see what I'm trying to do. Feel free to answer with your thoughts even though youre not sure, it might put me on the right track.

malmling
  • 2,398
  • 4
  • 19
  • 33

1 Answers1

40

Use scala.concurrent.{Future, Promise}:

def doAsyncAction: Promise[T] = {
  val p = Promise[T]
  p success doSomeOperation
  p
}

def useResult = {
   val async = doAsyncAction;
   // The return of the below is Unit.
   async.future onSuccess {
      // do action.
   };
};

Another way is to Await the result. (this is a blocking action).

Used when you need to return the result

import scala.concurrent.{ ExecutionContext, ExecutionContext$, Future, Promise, Await }
import scala.concurrent.duration._

def method: Option[T] = {
   val future: Future[T] = Future {
       someAction
   }
   val response = future map {
       items => Some(items)
   } recover {
       case timeout: java.util.concurrent.TimeoutException => None
   }
   Await.result(future, 5000 millis);
};

Be careful to execute blocking Futures in their own executor, otherwise you end up blocking other parallel computation.

flavian
  • 28,161
  • 11
  • 65
  • 105
  • Thank you for your answer it helped :) Though my solution that got it to work was to use a return a `Promise[Map[String, List[String]]]` and when I called it I checked that promises's future. And on that future's onSuccess, I did something. Maybe I should post my solution. If so let me know. – malmling Apr 08 '13 at 07:49
  • 1
    How do you execute a blocking function in its own executor? – franklin Jan 08 '19 at 18:19