Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Failing a Future based on a successful result

Tags:

scala

future

I have a scenario in my code where I need to make a Future fail based on a successful result that contains a certain value. I can make this work just fine via flatMap, but I want to know if there is a cleaner way to make this work. First, a very simplified example:

import concurrent._

case class Result(successful:Boolean)
object FutureTest {
  def main(args: Array[String]) {
    import ExecutionContext.Implicits._

    val f = Future{Result(false)}.flatMap{ result =>
      result match{
        case Result(false) => Promise.failed(new Exception("The call failed!!")).future
        case _ => Promise.successful(result).future
      }
    }

    f onFailure{
      case x => println(x.getMessage())
    }
  }
}

So in my example here, I want the Future to be failed if the Result returned has a value of false for its success indicator. As I mentioned, I can make this work okay with flatMap, but the line of code I would like to eliminate is:

case _ => Promise.successful(result).future

This statement seems unnecessary. The behavior that I would like is to be able to define the condition and if it evaluates to true, allow me to return a different Future as I am doing, but if it's not true, just leave things as they are (sort of like PartialFunction semantics. Is there a way to do this that I'm just not seeing? I've looked at collect and transform and those don't seem to be the right fit either.

Edit

After getting the map suggestion from @Rex Kerr and @senia, I created a PimpedFuture and an implicit conversion to pretty up the code a bit like so:

class PimpedFuture[T](f:Future[T])(implicit ex:ExecutionContext){
  def failWhen(pf:PartialFunction[T,Throwable]):Future[T] = {
    f map{
      case x if (pf.isDefinedAt(x)) => throw pf(x)
      case x => x
    }
  }
}

The implicit

  implicit def futToPimpedFut[T](fut:Future[T])(implicit ec:ExecutionContext):PimpedFuture[T] = new PimpedFuture(fut)

And the new handling code:

val f = Future{ Result(false) } failWhen {
  case Result(false) => new Exception("The call failed!!")
}

I think this is a little cleaner while still utilizing the suggestion to use map.

like image 666
cmbaxter Avatar asked May 22 '13 14:05

cmbaxter


People also ask

What future sequence does?

This Future. sequence() function converts a list of Futures into a single Future that means collections of Futures into a single Future. In simple words, List[Future[T]] ======> Future[List[T]] . It is also known as composing Futures.

What is Scala future?

Future represents a result of an asynchronous computation that may or may not be available yet. When we create a new Future, Scala spawns a new thread and executes its code. Once the execution is finished, the result of the computation (value or exception) will be assigned to the Future.

What is recover in Scala?

recover allows you to emit a final element and then complete the stream on an upstream failure. Deciding which exceptions should be recovered is done through a PartialFunction . If an exception does not have a matching case the stream is failed.


1 Answers

You can do this with less effort with map:

val f = Future{ Result(false) } map {
  case Result(false) => throw new Exception("The call failed!!")
  case x => x
}

but you still need to explicitly mention that you're passing the identity through.

Note that that you should not use andThen in such cases. andThen is for side effects, not for changing the result (unlike the Function1 method of the same name).

like image 195
Rex Kerr Avatar answered Sep 18 '22 12:09

Rex Kerr