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.
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.