A Callback is a Monad for doing in-thread non-blocking operations. It is
essentially a "function builder" that uses function composition to chain
together a callback function that is eventually passed to another function.
Normally if you have a function that requires a callback, the function looks something like:
def doSomething(param, param, callBack: result =>Unit)
and then you'd call it like
doSomething(arg1, arg2, result => println("got the result"))
This is the well-known continuation pattern, and it something we'd like to avoid due to the common occurrance of deeply nested "callback hell". Instead, the Callback allows us to define out function as
def doSomething(param1, param2): Callback[Result]
and call it like
val c = doSomething(arg1, arg2)
c.map{ result =>
println("got the result")
}.execute()
Thus, in practice working with Callbacks is very similar to working with Futures. The big differences from a future are:
1. Callbacks are not thread safe at all. They are entirely intended to stay inside a single worker. Otherwise just use Futures.
2. The execute() method needs to be called once the callback has been
fully built, which unlike futures requires some part of the code to know when a callback is ready to be invoked
Using Callbacks in Services
When building services, particularly when working with service clients, you
will usually be getting Callbacks back from clients when requests are sent.
*Do not call execute yourself!* on these Callbacks. They must be returned
as part of request processing, and Colossus will invoke the callback itself.
Using Callbacks elsewhere
If you are using Callbacks in some custom situation outside of services, be
aware that exceptions thrown inside a map or flatMap are properly caught
and can be recovered using recover and recoverWith, however exceptions
thrown in the "final" handler passed to execute are not caught. This is
because the final block cannot be mapped on (since it is only passed when
the callback is executed) and throwing the exception is preferrable to
suppressing it.
Any exception that is thrown in this block is however rethrown as a
CallbackExecutionException. Therefore, any "trigger" function you wrap
inside a callback should properly catch this exception.
A Callback is a Monad for doing in-thread non-blocking operations. It is essentially a "function builder" that uses function composition to chain together a callback function that is eventually passed to another function.
Normally if you have a function that requires a callback, the function looks something like:
and then you'd call it like
This is the well-known continuation pattern, and it something we'd like to avoid due to the common occurrance of deeply nested "callback hell". Instead, the
Callback
allows us to define out function asdef doSomething(param1, param2): Callback[Result]
and call it like
Thus, in practice working with Callbacks is very similar to working with Futures. The big differences from a future are:
1. Callbacks are not thread safe at all. They are entirely intended to stay inside a single worker. Otherwise just use Futures.
2. The execute() method needs to be called once the callback has been fully built, which unlike futures requires some part of the code to know when a callback is ready to be invoked
Using Callbacks in Services
When building services, particularly when working with service clients, you will usually be getting Callbacks back from clients when requests are sent. *Do not call
execute
yourself!* on these Callbacks. They must be returned as part of request processing, and Colossus will invoke the callback itself.Using Callbacks elsewhere
If you are using Callbacks in some custom situation outside of services, be aware that exceptions thrown inside a
map
orflatMap
are properly caught and can be recovered usingrecover
andrecoverWith
, however exceptions thrown in the "final" handler passed toexecute
are not caught. This is because the final block cannot be mapped on (since it is only passed when the callback is executed) and throwing the exception is preferrable to suppressing it.Any exception that is thrown in this block is however rethrown as a
CallbackExecutionException
. Therefore, any "trigger" function you wrap inside a callback should properly catch this exception.