I have an existing code which returns an instance of certain type.
def myMethod(inputs) = {
.....some calculations
MyInstance(....)
}
I have to make a change in it now. Change calls some service which returns a Future of some value, which I need to use to update MyInstance.
def myMethod(inputs) = {
.....some calculations
val futureWithSomeValue = someexternalservice.getData(....)
futureWithSomeValue.onComplete {
case Success(value) => ....create MyInstance
case Failure => ....throw error
}
}
But onComplete returns Unit, and hence it breaks the code.
What is best way to do it without changing method signature?
If myMethod call a Future then myMethod must return a Future
Thus, as was said in the comments, you may rather use map instead of onComplete to produce a new Future with your instance and return that.
def myMethod(inputs): Future[MyInstance] = {
// some calculations.
val futureWithSomeValue = someexternalservice.getData(....)
futureWithSomeValue.map { value =>
MyInstance(...)
}
Related
No need to pay attention to the purpose of the function here, it's only for demonstration:
def readAllByPersonOrFail(person: Person, otherPersonId: Long): Future[List[Person]] = {
val personSiblingsFuture: Future[List[Person]] = personSiblingsDomain.readAllByPersonId(person.id)
personSiblingsFuture.map { persons =>
persons.find(_.id == otherPersonId) match {
case Some(person) =>
person.isActive match {
case true => person
case false => throw new IllegalArgumentException("something inactive")
}
case None => throw new IllegalArgumentException("something wrong ehre")
}
}
personSiblingsFuture
}
I would like to return personSiblingsFuture above iff it validates (makes sure correct person is in the list and is active), otherwise throw the exception. I don't think the above code is doing the right thing as it is not existing upon failure.
Take a look at scala.concurrent.Future.map. This creates a new future, whose value is resolved by applying a function to the successful result of this future.
Note that here you're throwing away the resulting future you just created with .map() too.
There are a few areas to solve your problem, though you should question more deeply the use of exceptions with Futures. Scala provides concepts like Future, Option, and Try specifically to avoid throwing exceptions and have a clearer control flow.
Option 1, return the mapped future
In your funciton,
def func(...): Future[List[Person]] {
val personSiblingsFuture = ...;
personSiblingsFuture.map { persons =>
...
}
}
// note we're not returning personSiblingsFuture,
// but the mapped result
When someone actually tries to get the value of the future, e.g. by using .value, they might see an exception intead:
def main() {
val future = func(...); // this is fine
val my_list = future.value; // awaits Future, might throw here
}
Option 2, actually await the list and throw in the function
Returning a future that might throw is strange, it might be a bit easier if the you actually explicitly a had a function that might throw, e.g.
/** jsdoc describing function **/
def funcMightThrow(...): List[Person] {
val personSiblingsFuture = ...;
val personSiblings = personSiblingsFuture.value;
personSiblings.find(_.id == otherPersonId) match {
case Some(person) =>
person.isActive match {
case true => personSiblings
case false => throw new IllegalArgumentException("something inactive")
}
case None => throw new IllegalArgumentException("something wrong ehre")
}
}
Option 3, consider making return types more explicit
def func(...): Future[Try[List[Person]]] {
val personSiblingsFuture = ...;
personSiblingsFuture.map { persons =>
...
// successful case returns 'persons' (List[Person])
// fail cases return Failure(...) instead
}
} // return the mapped future
You can also return Try[List[Person]] rather than a Future[] of that, by using .value, which makes func a blocking function.
Consider a Model for Master/Slave election for a cluster.
Member{ id: Long, isMaster: Boolean }
I have a Dao/Repo with following methods:
MemberDao.findById(id:Long):Future[Option[Member]]
MemberDao.update(id:Long, member: Member):Future[Unit]
MemberDao.all() : Future[List[Member]]
Within the MemberService, I'm trying to write a function to set isMaster to false for all existing members, and I'm ending up with this crazily bloated code:
class MemberService ... {
def demoteAllMembers() : Future[Boolean] = {
val futures = memberDao.all.map{ memberFuture =>
memberFuture.map{ member =>
memberDao.findById(member.id).map { existingMemberFuture =>
existingMemberFuture.map { existingMember =>
memberDao.update(existingMember.id, existingMember.copy(isMaster = false)
}
}
}
val results = Await.result(futures, 10 seconds)
// return something here
}
}
}
My Questions are:
1. How should the return statement be written to handle success / errors? e.g. On success, return Future(true) and on failure, return Future(false)
2. Is this way of repetitively mapping future the correct way of doing async programming in scala? I understand this could be written differently in Actor paradigm and probably much better, but in case of OOP, is this the best Scala can do?
Thanks.
Why are you doing MemberDao.findById when you are already holding a member in hand??? (You are also treating the return as a Member, while it should really be an Option[Member]).
Also, update does not need to take an id as a separate parameter (there is one available inside member).
You don't need to Await your result, because your function is returning a Future, and you don't need to return a Boolean: just throw an exception to signal failure.
Consider something like this:
def demoteAllMembers: Future[Unit] = memberDao.all.flatMap {
Future.sequence(_.foreach {
memberDao.update(_.copy(isMaster = false))
})
}.map ( _ => () )
Not all that bloated, is it? :)
def returnFuture[A](x: A): Future[A] = {
val xFuture = Future { x } // suppose an API call that returns a future
xFuture.flatMap(x => {
println(x) // logging the value of x
xFuture
})
}
This is the way I'm currently doing it. To provide more context:
This function is being called inside an API when a request is made and I'd like the log message to be printed just before the value computed in the request is returned. Which is why, the following is not a good solution for me:
def returnFuture[A](x: A): Future[A] = {
val xFuture = Future { x } // suppose an API call that returns a future
xFuture.map(x => {
println(x) // logging the value of x
})
xFuture
}
Logging is a side-effect, meaning that you don't want the operation to fail if the logging fails for any reason (e.g. a call to toString throwing NPE).
Future#andThen is perfect for this use case. From the docs:
Applies the side-effecting function to the result of this future, and returns a new future with the result of this future.
This method allows one to enforce that the callbacks are executed in a specified order.
Note that if one of the chained andThen callbacks throws an exception, that exception is not propagated to the subsequent andThen callbacks. Instead, the subsequent andThen callbacks are given the original value of this future.
Your example becomes:
def returnFuture[A](x: A): Future[A] = {
Future { x } // suppose an API call that returns a future
.andThen { case Success(v) => println(v) }
}
You can use onComplete callback:
def returnFuture[A](x: A): Future[A] = {
val f = Future { x }
f.onComplete(println)
f
}
A map will work too:
def returnFuture[A](x: A): Future[A] = {
Future { x }.map { v =>
println(v)
v
}
}
Keep in mind that the whole point of using Futures is that you are trying to avoid blocking and that you don't control exactly when the Future will be executed. So, if you want more detailed logs while keeping the asynchronous nature of a Future, do something like this:
def doSomething(param: String): String = {
// log something here
val result = param.toUpperCase
// log something else here
result
}
def asFuture(param: String) = Future {
doSomething(param)
}
In other words, if this is an option, add logs to the x operation instead.
I am trying to get the following example working:
def asyncTest = Action {
val willBeInt = Future {
Thread.sleep(5000)
100
}
willBeInt.onComplete({
case Success(value) => Ok(s"Value = $value")
case Failure(e) => Failure(e)
})
}
But I am getting an error about overloading a method:
Overloaded method value [apply] cannot be applied to (Unit)
I'm coming from a background in NodeJS and am struggling to figure out how these callbacks are supposed to work while simultaneously returning a result to appease the method signature.
Think of Action as a function that returns a promise, rather than as a function that accepts a callback. In scala terminology, you'll be returning a Future. Play's internals will be calling onComplete (or something similar) on their own (analagous to a javascript Promise's then function).
Specifically, your compilation error is due to the fact that onComplete returns Unit, when the Action block is expecting you to return a Future. You can use map to transform your willBeInt future into what Play is looking for:
def asynTest = Action.async {
val willBeInt = Future {
Thread.sleep(5000)
100
}
// note you will probably need to
// import scala.concurrent.ExecutionContext.Implicits.global
// to be able to call `map` here
willBeInt map { value =>
Ok(s"Value = $value")
} recover {
case e: Throwable => InternalServerError(e.toString)
}
}
For some extra reading, check out the docs for Future, and the docs for Action
I know that this is not the Scala way of writing things. I think, in Scala you would use map. But I would like to write it this way, because it is more Java/c++ like.
However writing the following code the Scala compiler complains "method addGroup has return statement; needs result type".
Omitting the return and using an else branch works. But for formating reasons I'd like to use a return, because I don't want to indent the rest of the code which would happen if you use "else {}".
Where to add the result type. And is "Future[Result]" the correct type?
def addGroup = Action { implicit request =>
val optionUser = GetUserFromSession(request)
if (optionUser == None) {
return Redirect(routes.ApplicationUser.show(0))
}
Redirect(routes.ApplicationUser.show(optionUser.get.id))
}
You can't. The body of Action.apply is an anonymous function that you're trying to prematurely return from. The problem is, the return keyword in Scala returns from the inner-most named method, which this most certainly is not. So you'll be trying to return a Result where the method requires a Action[A].
The only way this can work is if you split the functions:
def addGroup = Action { implicit request =>
result(request)
}
// Could have a better name, but whatever, you shouldn't do this.
def result(request: Request): Result = {
val optionUser = GetUserFromSession(request)
if (optionUser == None) {
return Redirect(routes.ApplicationUser.show(0))
}
Redirect(routes.ApplicationUser.show(optionUser.get.id))
}
Using return makes code weird and difficult to read, so please don't.
If saving a single indentation is really a concern, what about this?
def addGroup = Action { implicit request =>
val optionUser = GetUserFromSession(request)
if (optionUser == None) Redirect(routes.ApplicationUser.show(0))
else Redirect(routes.ApplicationUser.show(optionUser.get.id))
}
Personally, I would re-write this using map and getOrElse:
def addGroup = Action { implicit request =>
GetUserFromSession(request) map { user =>
Redirect(routes.ApplicationUser.show(user.id))
} getOrElse {
Redirect(routes.ApplicationUser.show(0))
}
}
It removes the need to use .get and also prioritizes the positive branch.