0

In scala concurrent package, there is onComplete method which returns nothing while in akka-http directives, it returns the Directive1.

Is that the only difference? I sometimes got confused on those two methods.

Aamir
  • 2,380
  • 3
  • 24
  • 54

1 Answers1

1

These methods are different in what they aim to accomplish, but they are related in that the akka http onComplete is meant to work with the completion of a Future in order to complete the routing of a request.

The concurrent onComplete is a method on a Future that is meant to perform some sort of side effecting when the Future has completed. There are a lot of other methods on a Future like map, flatMap and filter that will produce another Future as you go through a series of transforms, but onComplete returns nothing, and is sort of a "dead end" in the processing of that Future.

An example of using onComplete while also showing how something like map can be used to change the Future is as follows:

val fut1 = Future{"1"} // A Future[String]
val fut2 = fut1.map(_.toInt) // Now a Future[Int]
fut2.onComplete{
  case util.Success(i) => println(s"success: $i")
  case util.Failure(ex) => ex.printStackTrace()
}

val badFut1 = Future{"A"}
val badFut2 = badFut.map(_.toInt) //Will be failed
badFut2.onComplete{
  case util.Success(i) => println(s"success: $i")
  case util.Failure(ex) => ex.printStackTrace()
}

In the first case, we will hit the success case as "1" can be mapped into an Int. In the second example, we will hit the error case as "A" cannot be mapped to an Int. Either way, you can see that something like map can be used to create a new transformed future while onComplete can be used to do something based on the final result of that Future.

In Akka Http, you sometimes need to create Route definitions that need to do something asynchronously. In that case, you need to complete the route when that asynchronous task completed. A Route itself needs to be fully defined in order to compile, so you can't leverage the existing onComplete on a Future because that returns nothing and that would render the definition of that Route as incomplete. Therefore, the guys at Akka defined an onComplete directive that will snap into a routing tree and work with a Future, returning a Directive that will fully define the tree for that Route as oppose to leaving it open ended as the Future.onComplete would. An example:

val myRoute = 
  get{
    val fut = myActor ? "foo"
    onComplete(foo){
      case util.Success(_) => complete(StatusCodes.OK)
      case util.Failure(ex) => complete(StatusCodes.InternalServerError)
    }
  }

So here, I'm making a request to an Actor using ask (?) and I get a Future back. I still need to produce something that completes the Route I am defining. If I did something like this:

val myRoute = 
  get{
    val fut = myActor ? "foo"
    fut.onComplete{
      case util.Success(_) => complete(StatusCodes.OK)
      case util.Failure(ex) => complete(StatusCodes.InternalServerError)
    }
  }

It worn't work because the onComplete here is the one from the Future and not the directive, and that returns Unit and my Route won't be full defined and won't compile.

So the onComplete directive is there to allow you to define a Route based on the completion of a Future which you can't do properly with the pre-existing onComplete from Future itself.

cmbaxter
  • 35,283
  • 4
  • 86
  • 95