When is it not possible to replace a Promise by Future? - scala

Just reading this article on promises, and the author uses the following example:
def redeemCampaignPledge(): Future[TaxCut] = {
val p = Promise[TaxCut]()
Future {
println("Starting the new legislative period.")
Thread.sleep(2000)
//p.success(TaxCut(20))
//println("We reduced the taxes! You must reelect us 2018!")
p.failure(LameExcuse("global economy crisis"))
println("We didn't fullfil our promises, so what?")
}
p.future
}
val taxCutF: Future[TaxCut] = redeemCampaignPledge()
println("Now they're elected, let's see if they remember their promise.")
taxCutF.onComplete {
case Success(TaxCut(reduction)) =>
println(s"Miracle! Taxes cut by $reduction percent.")
case Failure(ex) =>
println(s"They broke the promise again. Because of a ${ex.getMessage}")
}
My question is, can't I just get rid of Promises and rewrite it as:
def redeem(): Future[TaxCut] = Future {
println("Starting legislative period...!!!!")
Thread.sleep(2000)
println("We were successful")
TaxCut(25)
}
What is this second version lacking? I am not fully grasping the value that promises bring.

Yes, you are absolutely correct. this is a very common anti pattern with JavaScript Promises (where, Futures are called Promises and Promises are called deferred).
Basically, instead of using the continuations that futures provide, it is building a new continuation around them in a callback like fashion.

Related

Best practice to make cleanup after Scala Future is complete

I want to make some cleanup (like close db connection) after a Future is complete.
Currently I achieve it this way:
Future { ... } onComplete {
case Success(v) =>
// ...
conn.close()
case Failure(ex) =>
// ...
conn.close()
}
There's duplicate code and it's also tedious.
Is there any best practice to this?
Since the same action conn.close() is performed on both success and failure, consider executing it as a side-effect using andThen like so
Future { ... } andThen { _ => conn.close() }
Similarly, using onComplete we could do
Future { ... } onComplete { _ => conn.close() }
The difference between andThen and onComplete is that latter will return Unit, that is, discard the returned value of the Future.
The sad truth is that Scala Futures don't have this basic feature built in. I would therefore strongly suggest using a modern effect system like ZIO or cats-effect, both of which solve this problem and a myriad of others that Futures have. The easiest way to do what you want is to use the bracket method:
https://zio.dev/docs/overview/overview_handling_resources
Now bracket works great, but there's a way that usually works even better: the Managed type. It's virtually impossible to write code that leaks resources if you consistently use Managed when acquiring resources:
https://zio.dev/docs/datatypes/datatypes_managed
That said,if you absolutely must use Futures, you'll have to write your own try-finally equivalent. Or you can use mine:
def tryFinally[A](tryy: => Future[A])(finallyy: => Future[Any])(
implicit ec: ExecutionContext): Future[A] =
Future.fromTry(Try(tryy)).flatten.transformWith { t =>
finallyy.flatMap((_: Any) => Future.fromTry(t))
}
I like to use map, you can do something like this:
val mapped: Future[String] = future.map(_ => "OK").recover{case _ => "KO"}

Idiomatic way to handle side effect and return value in Scala functions

How would you construct a function in which you both want to do a side effect and return a value?
For example I would like the following function:
def futureFromHttpCall: Future[HttpResponse] =
doHttpCall.foreach(publishDomainEvent).returnOriginalFuture
(somehow I have a feeling that monads will come up so if that is the path Im somewhat familiar with cats if there is a solution for this problem there?)
The simplest thing I can think of is instead of "hiding" the side effect inside the Future[T] returning method, expose it as a continuation on the future:
def futureFromHttpCall: Future[HttpResponse] = doHttpCall
And then you could either onComplete on it as a side effect:
futureFromHttpCall.onComplete {
case Success(_) => publishDomainEvent
case Failure(e) => // Stuff
}
Making the effect explicit. Or if you're inside an actor system, you can can pipeTo the Future to your receive method and handle success / failure there.
I think your Future should only complete when all of your domain events are pushed. They should be a Future as well. Then you can use Future.sequence to wait for all of them to complete before returning.
Your question is a little unclear but i assume doHttpCall is a list of some type.
def doHttpCall(): Future[Seq[X]] = ???
def publishDomainEvent(x:X): Future[Unit] = ???
def futureFromHttpCall(): Future[Seq[X]] = {
val firstFuture = ???
firstFuture.flatMap { xs =>
val xxs: Seq[Future[Unit]]= xs.map(publishDomainEvent)
Future.sequence(xxs).map { _ => re }
}
}
All of this waiting can be pretty helpful when testing.

Clear onComplete functions list in Promise

This snippet
val some = Promise[Int]()
some.success(20)
val someFuture = some.future
someFuture.onSuccess {case i => println(i)}
someFuture.onComplete {case iTry => List(println(iTry.get*2), println(iTry.get*3))}
creates promise with list of 3 callbacks(List[CallbackRunnable]) on complete. Is there a way to clear this List or rewrite it?
This is kind of technically possible. But definitely not in the way that you want. If we execute a Future (or Promise) in one ExecutionContext, and the callback in another, we can kill the callback's ExecutionContext so that it can't complete. This works in the REPL (throws an exception somewhere), but is terrible idea to actually try in real code:
import scala.concurrent._
import java.util.concurrent.Executors
val ex1 = Executors.newFixedThreadPool(1)
val ex2 = Executors.newFixedThreadPool(1)
val ec1 = ExecutionContext.fromExecutorService(ex1)
val ec2 = ExecutionContext.fromExecutorService(ex2)
val f = Future { Thread.sleep(30000); println("done") }(ec1) // start in one ExecutionContext
f.onComplete { case t => println("onComplete done") }(ec2) // start callback in another ExecutionContext
ec2.shutdownNow
f.onComplete { case t => println("onComplete change") }(ec1) // start another callback in the original ExecutionContext
The first onComplete will not run (an exception is thrown in the background, which may or may not cause horrible things to happen elsewhere), but the second one does. But this is truly terrible, so don't do it.
Even if there was a construct to clear callbacks, it wouldn't be a good idea to use it.
someFuture.onComplete { case result => // do something important }
someFuture.clearCallbacks() // imaginary method to clear the callbacks
someFuture.onComplete { case result =>> // do something else }
The execution of this hypothetical code is non-deterministic. someFuture could complete before clearCallbacks is called, meaning both callbacks would get called, instead of just the second. But if it hasn't run yet, then only one callback will fire. There wouldn't be a nice way of determining that, which would lead to some truly horrible bugs.

scala's for yield comprehension used with Future. How to wait until future has returned?

I have a function which provides a Context:
def buildContext(s:String)(request:RequestHeader):Future[Granite.Context] = {
.... // returns a Future[Granite.Context]
}
I then have another function which uses a Context to return an Option[Library.Document]:
def getDocument(tag: String):Option[Library.Document] = {
val fakeRequest = play.api.test.FakeRequest().withHeaders(CONTENT_TYPE -> "application/json")
val context = buildContext(tag)(fakeRequest)
val maybeDoc = context.getDocument //getDocument is defined on Granite.Context to return an Option[Library.Document]
}
How would this code take into account if the Future has returned or not? I have seen for/yield used to wait for the return but I always assumed that a for/yield just flatmaps things together and has nothing really to do with waiting for Futures to return. I'm kinda stuck here and don't really no the correct question to ask!
The other two answers are misleading. A for yield in Scala is a compiler primitive that gets transformed into map or flatMap chains. Do not use Await if you can avoid it, it's not a simple issue.
You are introducing blocking behaviour and you have yet to realise the systemic damage you are doing when blocking.
When it comes to Future, map and flatMap do different things:
map
is executed when the future completes. It's an asynchronous way to do a type safe mapping.
val f: Future[A] = someFutureProducer
def convertAToB(a: A): B = {..}
f map { a => convertAToB(a) }
flatMap
is what you use to chain things:
someFuture flatMap {
_ => {
someOtherFuture
}
}
The equivalent of the above is:
for {
result1 <- someFuture
result2 <- someOtherFuture
} yield result2
In Play you would use Async to handle the above:
Async {
someFuture.map(i => Ok("Got result: " + i))
}
Update
I misunderstood your usage of Play. Still, it doesn't change anything. You can still make your logic asynchronous.
someFuture onComplete {
case Success(result) => // doSomething
case Failure(err) => // log the error etc
}
The main difference when thinking asynchronously is that you always have to map and flatMap and do everything else inside Futures to get things done. The performance gain is massive.
The bigger your app, the bigger the gain.
When using a for-comprehension on a Future, you're not waiting for it to finish, you're just saying: when it is finished, use it like this, and For-comprehension returns another Future in this case.
If you want to wait for a future to finish, you should use the Await as follows:
val resultContext = Await.result(context , timeout.duration)
Then run the getDocument method on it as such:
val maybeDoc = resultContext.getDocument
EDIT
The usual way to work with Futures however is to wait until the last moment before you Await. As pointed out by another answer here, Play Framework does the same thing by allowing you to return Future[Result]. So, a good way to do things would be to only use for-comprehensions and make your methods return Futures, etc, until the last moment when you want to finally return your result.
You can use scala.concurrent.Await for that:
import scala.concurrent.duration._
import scala.concurrent.Await
def getDocument(tag: String):Option[Library.Document] = {
val fakeRequest = play.api.test.FakeRequest().withHeaders(CONTENT_TYPE -> "application/json")
val context = Await.result(buildContext(tag)(fakeRequest), 42.seconds)
val maybeDoc = context.getDocument
}
But Await will block thread while future is not completed, so would be better either make buildContext a synchronous operation returning Granite.Context, or make getDocument async too, returning Future[Option[Library.Document]].
Once you are in a future you must stay in the future or you must wait until the future arrives.
Waiting is usually a bad idea because it blocks your execution, so you should work in the future.
Basically you should change your getDocument method to return a Future to something like getDocument(tag: String):Future[Option[Library.Document]]
Then using map ro flatMap, you chain your future calls:
return buildContext(tag)(fakeRequest).map(_.getDocument)
If buildContext fails, map will wrap the Failure
Then call
getDocument("blah").onComplete {
case Success(optionalDoc) => ...
case Failure(e) =>...
}

Are promises flawed?

Like the author of this question I'm trying to understand the reasoning for user-visible promises in Scala 2.10's futures and promises.
Particularly, going again to the example from the SIP, isn't it completely flawed:
import scala.concurrent.{ future, promise }
val p = promise[T]
val f = p.future
val producer = future {
val r = produceSomething()
p success r
continueDoingSomethingUnrelated()
}
val consumer = future {
startDoingSomething()
f onSuccess {
case r => doSomethingWithResult()
}
}
I am imagining the case where the call to produceSomething results in a runtime exception. Because promise and producer-future are completely detached, this means the system hangs and the consumer will never complete with either success or failure.
So the only safe way to use promises requires something like
val producer = future {
try {
val r.produceSomething()
p success r
} catch {
case e: Throwable =>
p failure e
throw e // ouch
}
continueDoingSomethingUnrelated()
}
Which obviously error-prone and verbose.
The only case I can see for a visible promise type—where future {} is insufficient—is the one of the callback hook in M. A. D.'s answer. But the example of the SIP doesn't make sense to me.
This is why you rarely use success and failure unless you already know something is bulletproof. If you want bulletproof, this is what Try is for:
val producer = future {
p complete Try( produceSomething )
continueDoingSomethingUnrelated()
}
It doesn't seem necessary to throw the error again; you've already dealt with it by packing it into the answer to the promise, no? (Also, note that if produceSomething itself returns a future, you can use completeWith instead.)
Combinators
You can use Promise to build additional Future combinators that aren't already in the library.
"Select" off the first future to be satisfied. Return as a result, with the remainder of the Futures as a sequence: https://gist.github.com/viktorklang/4488970.
An after method that returns a Future that is completed after a certain period of time, to "time out" a Future: https://gist.github.com/3804710.
You need Promises to be able to create other combinators like this.
Adapt Callbacks
Use Promise to adapt callback-based APIs to Future-based APIs. For example:
def retrieveThing(key: String): Future[Thing] = {
val p = Promise[Thing]()
val callback = new Callback() {
def receive(message: ThingMessage) {
message.getPayload match {
case t: Thing =>
p success t
case err: SystemErrorPayload =>
p failure new Exception(err.getMessage)
}
}
}
thingLoader.load(key, callback, timeout)
p.future
}
Synchronizers
Build synchronizers using Promise. For example, return a cached value for an expensive operation, or compute it, but don't compute twice for the same key:
private val cache = new ConcurrentHashMap[String, Promise[T]]
def getEntry(key: String): Future[T] = {
val newPromise = Promise[T]()
val foundPromise = cache putIfAbsent (key, newPromise)
if (foundPromise == null) {
newPromise completeWith getExpensive(key)
newPromise.future
} else {
foundPromise.future
}
}
Promise has a completeWith(f: Future) method that would solve this problem by automatically handling the success/failure scenarios.
promise.completeWith( future {
r.produceSomething
})