Asynchronous programming with Async / Await and the Scala Play Framework

Asynchronous programming has a number of advantages, most notably is its well touted ability to improve responsiveness. Asynchronous events occur independently of the main program flow and asynchronous actions are executed in a non-blocking, lock-free manner. This, ultimately, allows the main program flow to continue unimpeded, without blocking. On the flips side, asynchronous programming can be difficult to reason about. Many actions are often run simultaneously which can lead to complex semantics and interaction patterns.

Once example of such complexity is demonstrated in an infamous anti-pattern known as ‘Callback Hell’ or the “Pyramid of Doom”? This pattern was often observed in the early days of Node.js. Below, is a quick and dirty example:

asyncCall1(function () {
  asyncCall2(function () {
    asyncCall3(function () {
      asyncCall4(function () {
    })
  })
})

Fortunately, there are now far better mechanisms and patterns to effectively manage asynchronous code e.g. Futures, Promises, Observables, and Async / Await.

In the post, I will describe how Scala Async / Await can be used with the Play Framework and Scala to manage asynchronous code.

What is Async / Await?
Async and Await makes asynchronous events and actions far simpler to to code and reason about. The paradigm is best explained by a quote from Microsoft’s, Stephen Toub.

“You get to write your code using the language’s control flow constructs, just as you would if you were writing synchronous code”.

Essentially, you write code in a ‘seemingly’ synchronous style which leads to logical flow and code that is easy to reason about. Sounds great, right?

Async / Await in Action
Such paradigms are often best understood by examples. Thus, to demonstrate Async / Await, I have written a simple REST endpoint (with Play Framework and Scala) that uses Async / Await to fetch the user’s current location and then report on the weather at that location.

The code:

  • First, we define a REST endpoint for this service using the Play Framework’s routes.conf.
GET        /weather/local        controllers.Weather.local
  • Second, we create a Play Framework controller to drive this route. We will use Scala Async / Await to implement the business logic.

The logic is simple, first we asynchronously invoke a REST endpoint to fetch the user’s location. Once that response completes, we asynchronously execute a REST endpoint to fetch the weather data for the user’s location. We do all of this using Scala Async / Await, thus the main program flow NEVER blocks!

Here is the controller code.
I have also posted a gist here

class WeatherController @Inject() (ws: WSClient) extends Controller {
  /**
   * Get local weather
   * @return
   */
  def local = Action.async {
    async {
      val loc = await(getLocation)
      val r = await(getWeather(loc.json))
      Ok(r.json)
    }
  }

  def getWeather(loc: JsValue) = {
    val lat = (loc \ "latitude").as[Double]
    val lon = (loc \ "longitude").as[Double]
    ws.url(s"http://api.openweathermap.org/data/2.5/weather?lat=$lat&lon=$lon").get
  }

  def getLocation = {
    ws.url("http://www.telize.com/geoip").get
  }
}

Notice that the code appears to be written in a very synchronous manner. We first have a call to getLocation. Its result is assigned to loc. Then, we call getWeather. Its result is stored in r. Finally, we return the HTTP Ok status along with the result.

The code has a very synchronous feel that is ver easy to reason about. BUT, it purely asynchronous. There is no blocking!

How does it work?
The Async / Await constructs are essentially syntactic sugar. async marks a block of asynchronous code. Such a block usually contains one or more await calls, which marks a point at which the computation will be suspended until the awaited Future is complete.

Take aways
Asynchronous programming is an important concept with many benefits. As it’s grown in popularity and use, many new mechanisms and patterns have emerged to help developers simplify asynchronous complexity and more easily reason about their code. One of these patterns is Async / Await. We have seen how it can be put to good use within the context of Scala and the Play Framework. It enables developers to write asynchronous code in a style consistent with traditional synchronous programming .  In turn, this leads to a simplied logical flow that, at times, has escaped async code. Ultimately, code becomes easier understand and reason about which is critical to code sustainability and maintenance.

You may also like...

Leave a Reply