The Beginner’s Guide to Easy Parallel Programming With Scala Futures

philips
Reading Time: 3 minutes

Introduction

We can build a Futures API into the Scala programming language, making parallel programming much easier than using threads, locks, and callbacks. The purpose of this blog post is to provide an overview of Scala’s Futures: how they work, how you can use them, and how you can use them to leverage parallelism in your code.

Creating Futures

We have imported under the name Future the basic building block scala.concurrent.Future:

@ Future
res7: Future.type = scala.concurrent.Future$@2cdb1581

We can define Task as futures that run on a thread pool and return values after performing some computation. You can import Default thread pools in ExecutionContext.Implicits.global or you can also customize your own.

To create a future, we use the Future{ ... } syntax:

import scala.concurrent.ExecutionContext.Implicits.global
import scala.concurrent.Future

val f = Future{ "hello" + 123 + "world" }
println(f)

Output:
Future(Success(hello123world))

As can see here, the Future has already been completed and printed. In that case, it makes sense, since the computation is simple and would be complete almost immediately. Using Thread.sleep, we create Futures that take a bit longer:

import scala.concurrent.ExecutionContext.Implicits.global
import scala.concurrent.Future

val f = Future{ Thread.sleep(10000); "hello" + 123 + "world" }
println(f)

Output:
val f: scala.concurrent.Future[String] = Future(<not completed>)
Future(<not completed>)

It makes sense that the Future is listed as <not completed> since it should run in about 10,000 milliseconds. However, the line of code val f = completes instantly! Futures run in the background on a thread pool, so we can continue to do other things while they are running.

Awaiting Futures

It is possible to write to disk, store data in global variables, and perform work with Futures using side effects. But the simplest and most common case is for the Future to return one value at the end of the Future{ …… }.

import scala.concurrent.ExecutionContext.Implicits.global
import scala.concurrent.duration.Duration
import scala.concurrent.{Await, Future}

val f = Future{ Thread.sleep(10000); "hello" + 123 + "world" }
Await.result(f, Duration.Inf)
println(f)

Output:
val res0: String = hello123world
Future(Success(hello123world))

Await.result waits for a future to complete before extracting its value. It should have taken about 10 seconds (10,000 milliseconds) for aAwait.result to become ready, although Val f = complete and aAwait.result = completed instantly.

You don’t have to call Await.result immediately when you create a single Future in the background. Instead, your code can do other things while the Future runs, and only call Await.result when its other work is complete, and it finally needs to use the value returned by the Future.

Creating Multiple Futures

You can run multiple Futures at once because Futures run in the background. Here’s a code example

The following code shows how to use multiple futures in a for-expression. Prior to using them in the for-expression in step “(b)”, create your futures as shown in step “(a)”:

import scala.concurrent.ExecutionContext.Implicits.global
import scala.concurrent.Future
import scala.util.{Failure, Success}

object MultipleFutures extends App {

  //step-1 creating multiple futures
  val f1 = Future { sleep(100); 1 }
  val f2 = Future { sleep(200); 2 }
  val f3 = Future { sleep(300); 3 }

  //step-2 and run them simultaneously in a for-comprehension
  val result = for {
    r1 <- f1
    r2 <- f2
    r3 <- f3
  } yield (r1 + r2 + r3)

  result.onComplete {
    case Success(value) => println(s"\nresult = $value")
    case Failure(ex) => ex.printStackTrace()
  }

  sleep(3000)

  def sleep(time: Long): Unit = Thread.sleep(time)

}

Output:
result = 6

Conclusion

The Futures provide a way to reason about performing many operations in parallel– in an efficient and non-blocking manner. A Future is a placeholder object for a value that may not yet exist or can get at a certain time. In General, the value of the Future is supplied concurrently and can subsequently use. Composing the various concurrent tasks in this way tends to result in faster, asynchronous, non-blocking parallel code.

In the above blog we learned that if we wanted to see how to use multiple Scala Futures for comprehension, I hope this blog is helpful for you.

Written by 

Pappu Bhardwaj is a Software Intern at Knoldus Inc. in Noida. He has completed his B.Tech from the KIET Group of Institution, Ghaziabad. He is recognized as a good team player, a dedicated and responsible professional, and a technology enthusiast. He is a quick learner & curious to learn new technologies. He is passionate about Competetive Programming. He loves to play cricket and wildlife photography.