I'm having trouble with binding the result of observable A to a stream-observable B, such that:
Rx.Observable.concat(
A,
A.bind(valuesInA =>
B.scan((acc, value) => {
acc.push(value);
return acc;
}, valuesInA)))
So for example, if this is the case:
A: { onNext:[monkey], onComplete }
B: { onNext:banana, onNext:tree, onNext: ... }
I would like to get the observable:
AB: { onNext:[monkey],
onNext:[monkey, banana],
onNext:[monkey,banana,tree],
onNext: ... }
If there was a bind operator, and it called its callback when its callee was done, that would be great, because A finishes rather quickly (initial load of values) and then B streams the rest of them.
How do I compose this?
Notes: B.scan( ... ) : Observable<Array<apeness>> so I need to subscribe its result
Flatmap, scan, and startWith:
A.flatMap(a => B.scan((acc,v) => acc.concat(v), a).startWith(a))
.subscribe(...);
Related
Because I do some "complex" operations, I think my Actor became asyncronous. The main problem I think is that I use Await.result inside the method which return responses.
actor:
def process(subscribers: Set[ActorRef]): Receive = {
case Join(ref) => context become process(subscribers + ref)
case Leave(ref) => context become process(subscribers - ref)
case Push(request) =>
val filteredSubscribers = (subscribers - sender())
.filter(s => exists(s, request)) // just some actor filters
filteredSubscribers.foreach { subscriber =>
// here I have a Map with each actor requests
val actorOptions = getActorOptions(subscriber)
subscriber ? getResponse(actorOptions, request)
}
}
The problem is inside getResponse (I think).
getResponse(actorOptions: JsValue, request: SocketRequest): JsValue = {
(actorOptions \ "dashboardId").asOpt[Int] match {
case Some(id) => {
val response = widgetsService.getByDashboadId(id) map { widgets =>
val widgetsResponse: List[Future[String]] = widgets.map(w => {
widgetsService.getDataById(w.id) map {
data => s"""{ "widgetId": ${w.id}, "data": $data }"""
}
})
var responses: List[String] = List.empty
widgetsResponse.foreach(f => {
f.onComplete {
case Success(value) => responses = value :: responses
case Failure(e) => println(s"Something happened: ${e.getMessage}")
}
})
// first time when I use Await.result
// used to populate the responses list with data from all futures
Await.result(Future.sequence(widgetsResponse), Duration.Inf)
Json.parse(s"""{
"dashboardId": $id,
"widgets": [${response.mkString(", ")}]
}""".stripMargin)
}
// second time when I use Await.result
// used to return a JsValue instead of a Future[JsValue]
Await.result(response, Duration.Inf)
}
case None => buildDefaultJson // return default json value, unimportant for this example
}
}
Due of that, In frontend, if I have 2 sockets clients, the response for the second will be send only after first.
I found that I can obtain a "fake" increase of performance if I embrance the getResponse in a future inside of my Actor.
filteredSubscribers.foreach { subscriber =>
val actorOptions = getActorOptions(subscriber)
Future(subscriber ? getResponse(actorOptions, request))
}
So, for both subscribers the action will be started in same time, but when the first will reach the Await.result, the second will be locked until first is done.
I need to avoid using Await.result there, but I don't know how to get the results of a list of futures, without using for-comprehension (because is a dynamically list) for first time where I use it.
Because Akka ask operator (?) return a Future[Any], I tried that my getResponse method to return directly a JsValue to be mapped then in Future[JsValue]. If I remove the second Await.result and my method will return Future[JsValue], then the actor will return a Future[Future[JsValue]] which I don't think is too right.
After some more researches and solutions found on so, my code become:
Future.sequence(widgetsResponse) map { responses =>
Json.parse(
s"""
|{
|"dashboardId": $id,
|"tableSourceId": $tableSourceId,
|"widgets": [ ${responses.mkString(", ")}]
|}""".stripMargin
)
}
getResponse returns a Future[JsValue] now, removing both Await.result cases, and actor case become:
filteredSubscribers.foreach { subscriber =>
val actorOptions = getActorOptions(subscriber)
getResponse(actorOptions, request) map { data =>
subscriber ? data
}
}
I don't know why, still have a synchronous behavior. Damn, this can be due of my subscribers type: Set[ActorRef]? I tried to use parallel foreach and this looks like solving my problem:
filteredSubscribers.par.foreach { subscriber =>
val actorOptions = getActorOptions(subscriber)
getResponse(actorOptions, request) map { data =>
subscriber ? data
}
}
I have actually a problem while returning a Seq back to frontend.
My code looks like this:
def getContentComponentsForProcessSteps: Action[AnyContent] = Action.async { implicit request =>
println("-----------------------------------------------New Request--------------------------------------------------------")
println(request.body.asJson)
request.body.asJson.map(_.validate[ProcessStepIds] match {
case JsSuccess(steps, _) =>
val contentComponents: Seq[Future[Seq[Future[ContentComponentModel]]]] = steps.steps.map(stepId => { //foreach
// Fetching all ContentComponent Relations
contentComponentDTO.getContentComponentsByStepId(stepId).map(contentComponents => { // Future[Seq[ContentComponent_ProcessStepTemplateModel]]
// Iteration über die Gefundenen Relations
contentComponents.map(contentComponent => { // foreach
// Fetch Content Component
contentComponentDTO.getContentComponentById(contentComponent.contentComponent_id).flatMap(contentComponent => { // Future[Option[ContentComponentModel]]
// Fetch Content Component Data for the types
val res = getContentComponentDataforOneContentComponent(contentComponent.get)
res.map(con => con)
})
})
})
})
Future.sequence(contentComponents).map(eins => {
println(eins)
Ok(Json.obj("Content Components Return" -> "true", "result" -> eins))
})
case JsError(_) =>
Future.successful {
BadRequest("Can't fetch Content Components")
}
case _ => Future.successful {
BadRequest("Can't fetch Content Components")
}
}).getOrElse(Future.successful {
BadRequest("Can't fetch Content Components")
})
}
Error is the following.
Thanks for any hint
Look at the type of eins your error message is telling you that it is a Seq[Seq[Future[ContenetComponentModel]]] and not simply a Seq like you thought.
There are two problems with this:
You can't write a Future (or in your case, a sequence of futures) to Json.
You need to have an implicit function in scope to convert your ContenetComponentModel to a JSON value.
Depending on what you want your result to look like, you could try flattening eins and then using another Future.sequence, but I think what you really should be doing is changing a lot of your .map calls to .flatMap calls to avoid the nesting in the first place.
I'm trying to figure out how to link multiple asynchronous calls and return a result. I am currently trying to asynchronously user data first, and update user data asynchronously and return result, but it seems like it is not working :(
i used map { result => Ok(result)}, but play still thinks that I am returning an object. any help?
def updateUserData() = Action.async { implicit request =>
updateUserForm.bindFromRequest.fold(
errors => Future.successful(BadRequest(views.html.authenticated.settings.settings_hero(errors, Option(""), Option("")))),
{
case (userData) =>
request.session.get("email") match {
case Some(email) =>
getUser(email, userData.curent_password) map { userCheck =>
if (userCheck) {
updateUserOnService(email, userData.f_name, userData.l_name, userData.new_password) map { result =>
Ok("please")
}
//val e = updateUserOnService(email, userData.f_name, userData.l_name, userData.new_password) map {result => Ok("")}
// user is valid now update the user data
// call removeAuth to log out
// redirect to home
///Ok (updateUserOnService(email, userData.f_name, userData.l_name, userData.new_password) map { result => result})
//Redirect(routes.settings.index()).addingToSession("email" -> email)
} else {
BadRequest(views.html.authenticated.settings.settings_hero(updateUserForm.bindFromRequest.withGlobalError(Messages("error.login", email)), Option(""), Option("")))
}
}
}
})
}
The main part that i am having issue is this part. I think it is matter of some syntax. Could someone help?
Thanks
updateUserOnService(email, userData.f_name, userData.l_name, userData.new_password) map { result =>
Ok("please")
}
The issue is with your types and that they don't match up with the required ones.
.fold has to result in Future[Result] in both branches (the error and the success ones).
In the successful form bind branch you have this:
case (userData) => ... // The ... must evaluate to Future[Result]
Looking at your first operation we see:
request.session.get("email") match {
case Some(email) => ...
}
One big issue here is that the None case is not handled! (but this is does not cause the types not matching up). Having something like the following will solve this: case None => Future.successful(BadRequest(...))
So moving on: in the Some you have the following:
getUser(email, userData.curent_password) map { userCheck =>
if (userCheck) {
updateUserOnService(email, userData.f_name, userData.l_name, userData.new_password) map { result =>
Ok("please")
}
} else {
BadRequest(views.html.authenticated.settings.settings_hero(updateUserForm.bindFromRequest.withGlobalError(Messages("error.login", email)), Option(""), Option("")))
}
}
This is where the issue is:
getUser will return with a Future[X] and when you map over it you will have Future[Y] where Y will be what userCheck => ... evaluates to.
In this case the types are totally mixed up, since when you do if(usercheck) on the true branch you have Future[Result] on the false branch you have Result. So the types don't align on both branches which is a big issue and the compiler will infer Any from this.
To fix this, in the false branch create a future: Future.successful(BadRequest(....))
Ok, now that we fixed the most inner type issues, let's start going backwards. Inside we have Future[Result], if we go back one level (before the getUser()) then we will have Future[Future[Result]]. Again this is not what we want, because we need Future[Result].
The solution to this is to flatMap instead of map, because with flatMap when you need to return with the same container type and it flattens it. A quick example to understand this:
Seq(1, 2, 3).flatMap(i => Seq(i, i))
// res0: Seq[Int] = List(1, 1, 2, 2, 3, 3)
In the case of Futures:
import scala.concurrent.Future
import scala.concurrent.ExecutionContext.Implicits.global
Future(1).flatMap(i => Future(i*2))
// res1: scala.concurrent.Future[Int] = [....]
So we see that we don't have double nesting, but just a single Future.
Going back to your example this would be my updated code that would work better:
def updateUserData() = Action.async { implicit request =>
updateUserForm.bindFromRequest.fold(
errors => Future.successful(BadRequest(views.html.authenticated.settings.settings_hero(errors, Option(""), Option("")))),
{
case (userData) =>
request.session.get("email") match {
case Some(email) =>
getUser(email, userData.curent_password).flatMap { userCheck =>
if (userCheck) {
updateUserOnService(email, userData.f_name, userData.l_name, userData.new_password) map { result =>
Ok("please")
}
} else {
Future.successful(BadRequest(views.html.authenticated.settings.settings_hero(updateUserForm.bindFromRequest.withGlobalError(Messages("error.login", email)), Option(""), Option(""))))
}
}
case None => Future.successful(BadRequest) // FIXME: Implement as you wish
}
})
}
In the following example which is from one of the books that I'm currently reading about Observables:
object ObservablesCreate extends App {
val vms = Observable.create[String] { obs =>
 obs.onNext("JVM")
obs.onNext("DartVM")
obs.onNext("V8")
obs.onCompleted()
Subscription()
}
vms.subscribe(log _, e => log(s"oops - $e"), () => log("Done!"))
}
The text that follows hints that the above code snippet has a synchronous subscribe method which is very much understandable. My question is that isn't the whole purpose of using Observables to do asynchronous event emitting? I mean here is does not differ to an Iterable. Is this explained for the sake of some explanation?
Yes, it is only for the sake of explaining the create factory method. In the snippet you are generating the elements synchronously when a subscriber subscribe.
But in the same way you can call onNext asynchronously, for example when a Future resolves:
object ObservablesCreate extends App {
def future: Future[String] = ???
val vms = Observable.create[String] { obs =>
val f = future
f onComplete {
case Success(s) => {
obs.onNext(s)
obs.onCompleted()
}
case Failure(exception) => obs.onError(exception)
}
Subscription()
}
vms.subscribe(log _, e => log(s"oops - $e"), () => log("Done!"))
}
(For this specific case however is much better to use the Observable.from method to convert a Future to an Observable)
I want to have some action completed ONCE, when two promises are success. So I created such a piece of code:
val promiseForPersistence = Promise[Unit]()
val promiseForReplication = Promise[Unit]()
promiseForPersistence.future.onComplete {
case scala.util.Success(_) => {
if (promiseForReplication.isCompleted) {
println(s"Acking from persistence promise (replication one already completed)" )
}
}
case _ =>
}
promiseForReplication.future.onComplete {
case scala.util.Success(_) => {
if (promiseForPersistence.isCompleted) {
println(s"Acking from replication promise (persistence one already completed)" )
}
}
case _ =>
}
promiseForPersistence.success()
promiseForReplication.success()
The problem is, both log messages are printed. I want the action to be fired only once. Why it is fired twice, and how can I have it fire only once?
You should not use isCompleted method on Future for this purpose. You should combine 2 futures in a single future using flatMap like this:
for {
_ <- promiseForPersistence.future
_ <- promiseForReplication.future
} println("Both are completed" )
Callback in onComplete could be evaluated after both Promises are completed. So you'll get isCompleted == true in both callbacks and 2 println messages.
Valid way is to add callback in callback:
promiseForPersistence.future.onSuccess { _ =>
promiseForReplication.future.onSuccess { _ =>
println(s"Acking from replication promise (persistence one already completed)" )
}
}
You'll get exactly the same behavior with flatMap (for-comprehension), but with less verbose syntax.