hminle
hminle

Reputation: 521

What is the difference between onComplete and foreach for a future in Scala?

I wonder the difference between onComplete and foreach when we use a Future in Scala.

f onComplete (_ => doSomething(_))

and

f foreach (_ => doSomething(_))

  1. Do the above lines of code lead to the same result?

  2. If I want to doSomething with Future f only after it's completed. What should I do? Should I use isCompleted like this:

    if(f.isCompleted) f onComplete (_ => doSomething(_))

Thank you guys a lot

Upvotes: 13

Views: 7370

Answers (1)

Giovanni Caporaletti
Giovanni Caporaletti

Reputation: 5556

The main difference is the onComplete callback gets called even if the future completes with a failure, while foreach (and onSuccess) functions are called only in case of a successful result.

In fact, onComplete's parameter is a function Try[T] => U: the function you pass will be called with a Success[T] as argument if the future is successful or with a Failure if there was an exception:

val f = Future { ??? } // this future completes with a failure

// foreach only calls the callback if the future is successful
f.foreach(_ => thisWillNeverExecute()) 

// This will print "future failed" after the future completes
f.onComplete {
  case Success(_) => println("future completed successfully")
  case Failure(e) => println("future failed")
}

Also, you don't need to check anything to call the mentioned methods: onComplete/onSuccess/onFailure/foreach all schedule a callback that is called on the implicit ExecutionContext you have in scope, only when the future completes.

You can call them even if isCompleted is false, they will only be executed when the future completes successfully or fails, depending on which one you chose.

Let's have a look at their signatures:

def onComplete[U](@f: Try[T] => U)(implicit executor: ExecutionContext): Unit
  • onComplete takes a Try[T] => U function: this function will be executed on the implicit executor when the future completes. The argument will either be a Success[T] if the future is successful or a Failure if the future is failed

def onFailure[U](pf: PartialFunction[Throwable, U])(implicit executor: ExecutionContext): Unit
  • onFailure parameter is a PartialFunction that is only executed on the implicit executor if the future fails with a Throwable for which pf is defined. It's basically the same as calling onComplete matching only some Failures, and in fact that is exactly how it is implemented in the standard library.

def onSuccess[U](pf: PartialFunction[T, U])(implicit executor: ExecutionContext): Unit
  • onSuccess parameter is, symmetrically to onFailure, a PartialFunction that is only executed on the implicit executor if the future completes successfully and the supplied PartialFunction is defined for the value.

def foreach[U](f: T => U)(implicit executor: ExecutionContext): Unit
  • foreach is basically the same as onSuccess but it takes a total function. This means that f is always executed if the Future completes successfully

N.B.: 'onSuccess' and 'onFailure' are going to be deprecated in 2.12. I suggest you read this series of posts by Viktor Klang to find out how you will be affected by the changes

Upvotes: 22

Related Questions