Callback

You are viewing the documentation for the older Monix 2.x series.
For the latest version: see here!

Callback is a listener type that can be called asynchronously with the result of a computation.

When building an asynchronous Task, on execution the API gives you a Callback instance that you can invoke with the result of a computation, on completion. Its definition is something like:

trait Callback[-T] extends (Try[T] => Unit) {
  def onSuccess(value: T): Unit
  def onError(ex: Throwable): Unit
}

This callback type has a contract:

  1. on completion, if it was a successful execution, one needs to call onSuccess
  2. on completion, if the execution ended in error, one needs to call onError
  3. after calling onSuccess or onError then no more calls are allowed, as that would be a contract violation

In order to protect the contract, you can wrap any such callback into a “safe” implementation that protects against violations:

import monix.eval.Callback

val callback = new Callback[Int] {
  def onSuccess(value: Int): Unit = 
    println(value)
  def onError(ex: Throwable): Unit =
    System.err.println(ex)
}

// We need an exception reporter, but we can just use a Scheduler
import monix.execution.Scheduler.Implicits.global

val safeCallback1 = Callback.safe(callback)

// But really, we don't need a Scheduler
import monix.execution.UncaughtExceptionReporter
import UncaughtExceptionReporter.{LogExceptionsToStandardErr => r}

val safeCallback2 = Callback.safe(callback)(r)

NOTE: when executing Task.runAsync(callback), the provided callback is automatically wrapped in Callback.safe, so you don’t need to worry about it.

In case you just want an empty callback that doesn’t do anything on onSuccess, but that can log errors when onError happens, maybe because you just want the side-effects:

val task = monix.eval.Task(println("Sample"))
// task: monix.eval.Task[Unit] = FlatMap(
//   Async(monix.eval.Task$$$Lambda$9215/0x0000000802e61040@3aa00e8d),
//   monix.eval.Task$$$Lambda$9216/0x0000000802e80040@29ad3767
// )

task.runAsync(Callback.empty)
// res1: monix.execution.Cancelable = monix.execution.cancelables.StackedCancelable@649e7b07

Or maybe you want to convert a Scala Promise to a Callback:

val p = scala.concurrent.Promise[String]()
// p: concurrent.Promise[String] = Future(<not completed>)

val callback = Callback.fromPromise(p)
// callback: Callback[String] = <function1>

An interesting effect when dealing with callbacks is that callbacks calling other callbacks can quickly and easily lead to stack-overflow errors. So to force a protective asynchronous boundary when calling onSuccess or onError (which may or may not fork a thread):

// Lets pretend we have something meaningful
val ref = Callback.empty[String]

val asyncCallback = Callback.async(ref)