how get the result of Future without using "await" - scala

I want to get the result of a Future without blocking operations.
If I write my code with "await", it works but it is not good for me because it is blocking:
val t: Future[MatchResult[Personne]] = db.getPersonne(userId).map(_.get must beEqualTo(personne))
t.await
I tried to change my code with map:
val r: Future[MatchResult[Personne]] = db.getPersonne(userId).map(_.get must beEqualTo(personne))
r.map {
case r#isWhatIExpected => r
case isNot => isNot
}
but I have this error:
found :
scala.concurrent.Future[org.specs2.matcher.MatchResult[Personne]]
[error] required: org.specs2.specification.create.InterpolatedFragment

Using Specs2 as it appears, there are helpers to test async functions.
import org.specs2.concurrent.{ExecutionEnv => EE}
"Foo" in { implicit ee: EE => // take care of ee
myAsyncFunWithFuture must beEqualTo(expectedVal).await(timeToWait)
}

#user4650183 I think I don't understand your question then. Something somewhere has to wait, or block if you prefer, on the result before using it.

Related

Await.result on HttpService

I've a scala project with http4s 0.15.16a and slick 3.2.1 with these steps:
Receive a ID by rest call
passing ID to MySlickDAO that responds with a Future
Call Await.result(res, Duration.Inf) on Future returned by MySlickDAO
Create the json
The problem is that I use a Await.result and this is bad practices
is there a better solution ?
Here the code:
val service = HttpService {
//http://localhost:8080/rest/id/9008E75A-F112-396B-E050-A8C08D26075F
case GET -> Root / "rest" / "id" / id =>
val res = MySlickDAO.load(id)
Await.result(res, Duration.Inf)
val ll = res.value.get.get
ll match {
case Failure(x) =>
InternalServerError(x)
case Success(record) =>
val r = record.map(x => MyEntity(x._1, x._2, x._3))
jsonOK(r.asJson)
}
case ....
}
Instead of awaiting, you can chain the result of one Future into another:
val resFut = MySlickDAO.load(id)
resFut.map { record =>
val r = record.map(x => MyEntity(x._1, x._2, x._3))
jsonOK(r.asJson)
} recover { x =>
InternalServerError(x)
}
The result of this will be a Future of a common supertype of jsonOK and InternalServerError (not familiar with the libraries you're using; so I may have the type of load wrong: it's not a Future[Try[_]] is it?).
BTW: your original code has a very problematic line:
val ll = res.value.get.get
res.value is an Option[Try[T]]. Calling get on an Option or a Try is generally a bad idea (even though in this case because of the Await, the Option should never be None, so the get is technically safe) because it can throw an exception. You're much better off using map, flatMap, and friends.
The issue is that http4s 0.15 uses the Scalaz concurrency constructs, while Slick uses the native Scala ones, and the two aren't designed to work with each other. My understanding is that http4s 0.17+ has switched from Scalaz to Cats, which might entail using native Scala Futures, so if you can upgrade that might be worth a shot. If not, you can handle the conversion by manually creating a Task that wraps your future:
def scalaFutureRes = MySlickDAO.load(id)
val scalazTaskRes = Task.async { register =>
scalaFutureRes.onComplete {
case Success(success) => register(success.right)
case Failure(ex) => register(ex.left)
}
}
At this point you've got a Task[ResultType] from the Future[ResultType] which you can map/flatMap with the rest of your logic like in Levi's answer.
You can also use the delorean library for this which has this logic and the opposite direction defined on the classes in question via implicit conversions, so that you can just call .toTask on a Future to get it in a compatible form. Their readme also has a lot of useful information on the conversion and what pitfalls there are.

Dealing with errors using idiomatic Scala

I'm writing an HTTPS service for a chat bot and find myself dealing with a lot of Futures and Options. Usually if an Option returns None or a Future fails I want to log the exception and reset the user back to the start. Here's a toy example of how I accomplish this:
(for {
user <- userService.retrieve(userId)
userPet <- Future(user.userPet.get)
_ <- sendTextAsJson(s"You're holding a $userPet!")
} yield {}).recover {
case ex: Exception =>
log.error(ex.toString)
fail
}
This works fine but it feels a little weird to wrap things in Future just so their exceptions are swallowed and dealt with in the recover block. It also feels weird to include an empty yield block. Is there a better way?
What you basically do is using onSuccess or onFailure to retrieve the futures result. What you also might try is Try.
There is an example of the underlying functionality.
http://www.scala-lang.org/api/2.9.3/scala/util/Try.html
I might suggest you this article: http://docs.scala-lang.org/overviews/core/futures.html I can't summarize whats stated there in a few sentences. But if you look to the table of contents on the right side, the point Futures explains what happens and how to handle it is stated under Excepetions. Thats the idiomatic way.
I don't think it's too bad tbh, assuming userService.retrieve() returns a Future in the first place. I'd personnally rather use map in this case to make things a bit more explicit :
val futureMsg = userService.retrieve(userId)
.map(user => sendTextAsJson(s"You're holding a ${user.userPet.get}!")
.recover {
case NonFatal(ex) => //Look it up ;)
log.error(ex.toString)
fail
}
You now have Future[Unit] to do whatever you want with.
I agree with you that that's an awkward use of a for-comprehension. This is how I would write this:
import scala.util.control.NonFatal
userService.retrieve(userId)
.map(_.userPet.get)
.map(userPet => s"You're holding a $userPet!")
.flatMap(sendTextAsJson)
.recover {
case NonFatal(ex) =>
log.error(ex.toString)
fail
}
Looking at your sendTextAsJson and fail functions you seem to want to side-effect when the future completes. I would not use map after you perform the Option.get and instead look at Futures onComplete method to either handle the success of the future or handle the exception throw. I am not sure how this way and recover are different though so double check that. I did like #sascha10000 link to the scala doc futures. Its been a long time since I read that but its a good resource from what I remember
Example implementation of your code with onComplete:
import scala.concurrent.Future
import scala.util.{Failure, Success}
import scala.concurrent.ExecutionContext.Implicits.global
object UserFuture extends App {
def fail = println("failed")
def sendTextAsJson(message: String): Unit = println(message)
case class User(userPet: Option[String])
object userService {
def userPet = Some("dog")
val someUser = User(userPet)
def retrieve(userId: Int): Future[User] = Future {
someUser
}
}
def getPet(userId: Int): Unit = {
userService.retrieve(userId)
.map(user => user.userPet.get)
.onComplete {
case Success(userPet) => sendTextAsJson(s"You're holding a $userPet!")
case Failure(ex) => println(ex.toString); fail
}
}
getPet(1)
Thread.sleep(10000) // I forgot how to use Await. This is just here to be able to make sure we see some printouts in the console.
}

Type mismatch mapping Future[Seq[model]] using Play with Scala

I still struggle sometimes to map Futures using Play with Scala...
I am trying to pass to a view my entire Supply DB table (i.e. all the supplies in the DB).
I have tried two distinct ways but both have failed...
Below are the methods I've tried and the errors I get.
Can someone please help me solve this, and also explain me why both of these have failed?
Thank you in advance!
Note: Calling supplyService.all returns a Future[Seq[Supply]].
First attempt
def index = SecuredAction.async { implicit request =>
supplyService.all map { supplies =>
Future.successful(Ok(views.html.supplies.index(request.identity, SupplyForm.form, supplies)))
}
}
Second attempt
def index = SecuredAction.async { implicit request =>
val supplies = supplyService.all
Future.successful(Ok(views.html.supplies.index(request.identity, SupplyForm.form, supplies)))
}
First variant without Future.succesfull
supplyService.all.map( supplies => Ok(views.html.supplies.index(request.identity, SupplyForm.form, supplies)) )
Since you have can construct function Seq[Supply] => Result you can easily map
Future[Seq[Supply]] to Future[Result] via functor interface.
Future is also a monad , so you can use Seq[Supply] => Future[Result] with flatMap method.
But Future.successfull is monad unit , and as for many monads for Future its true that
mx.flatMap(f andThen unit) = mx.map(f)
so your
ms.flatMap(supplies => Future.successfull(f(supplies)) =
ms.flatMap(f andThen Future.successfull) =
ms.map(f)

Spray: factor out onSuccess directive

I would like to change the following code fragment in a way that I want to factor out the onSuccess block into a new method. JSON marshalling should still work.
(patch & parameterMap & asJson) { params =>
...
val f:Future[ResposeData]=createResponse(...)
onSuccess(f){complete(_)}
}
I would like to have a method like:
def handleSuccess(f:Future/FutureMagnet)(implicit ...)
A simple refactoring doesn't work for me. I tried a lot of combinations but I can't find either the correct signature or the code working.
Example:
def handleSuccess(f: Future[ResposeData]): Unit = {
onSuccess(f) { complete(_) }
}
Error:(43, 15) type mismatch;
found : scala.concurrent.Future[ResponseData]
required: spray.routing.directives.OnSuccessFutureMagnet
onSuccess(f) {
^
If I now change signature I get another error:
def handleSuccess(f: OnSuccessFutureMagnet)
Error:(44, 18) spray.routing.Directive[f.Out] does not take parameters
onSuccess(f) {
^
Maybe this is just a simple thing to do but I'm new to spray.
So it would be nice if somebody could give a hint.
Thanks
onSuccess takes a function, basically when you use that on your future the value inside the future becomes available and you can complete your route using that value if you want:
case class ResponseData(data: String)
def handleSuccess(f: Future[ResponseData]): Unit = {
onSuccess(f) { responseData =>
complete(_)
}
}
From the comments:
"Unwraps" a Future[T] and runs its inner route after future completion with the future's value as an extraction of type T.
Note also that
If the future fails its failure throwable is bubbled up to the nearest ExceptionHandler.
Maybe you want to use onComplete which returns a Try and you can then match on Success or Failure.

Scala: "required: scala.concurrent.Future[?]"

Edit
Still haven't found a solution so I ended up creating two someFuture methods. One that returns a future & one that doesn't (to get otherFuture to compile)
I'm trying to return Future[Option[JsObject]] but keep getting this error:
required: scala.concurrent.Future[?]
What I'm doing
def someFuture:Future[Option[JsObject]] =
Future {
Option(JsObject())
}
def otherFuture:Future[Option[JsObject]] =
Future {
Option(JsObject(
someFuture.flatMap(_.get)
))
}
// get error here
found : JsObject
[error] required: scala.concurrent.Future[?]
How can I return the JsObject without getting an error?
The problem is that someFuture.flatMap(_.get) won't compile—you need to provide a function that takes a JsObject and returns a Future[Whatever] to use flatMap on someFuture.
You probably want something like this:
def otherFuture: Future[Option[JsObject]] = someFuture.map { opt =>
Option(JsObject(opt.get))
}
There's not really any reason to use Option if you're just going to call .get on it like this, though, so the following might be better:
def otherFuture: Future[Option[JsObject]] = someFuture.map(_.map(JsObject(_)))
Now if the future is satisfied by a non-empty option, the contents of the option will be wrapped in another layer of JsObject, which seems to be what you're aiming for?
Note that if you're using Option to represent failure, you may want to consider the failure-handling that's built into Future instead.