aboutsummaryrefslogtreecommitdiff
path: root/doc/tutorial.md
blob: f509fa68fa2ab56d493c9fa8b104d4a53e340484 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
# Async Tutorial

By Philipp Haller

## Preliminaries

Async is a macro library for Scala 2.10.1 or higher. (Since it's
slated for inclusion in the Scala 2.11 distribution there will be
binary artifacts for all upcoming Scala 2.11 releases.) To start using
Async, it suffices to include a compatible binary jar on the
classpath.

To use Async with the default binding for Scala's futures, the members
`async` and `await` of the `scala.async.Async` object have to be
imported. (In a future stable release, these members might be moved
to the `scala.async` package object.) A language import to enable
macros is not required for users of async/await.

## The `async` Construct

The `async` construct has the following signature:

    def async[T](body: => T): Future[T]

As can be seen from its type, it creates a future; `body` is a by-name
parameter which means that it is evaluated asynchronously as the body
of the future. Calling `async` requires having an implicit
`ExecutionContext` in scope on which the new future is scheduled for
execution. If there is no such `ExecutionContext` in scope,
compilation fails. (Similar to how `future { ... }` works.)

The body of `async` may contain calls to `await` which is explained next.

## The `await` Construct

Calling `await` inside the body of an `async` block suspends the
evaluation of the `async` block until a given future is completed
(successfully or unsuccessfully). Its signature is as follows:

    def await[T](fut: Future[T]): T

Even though the signature and semantics of `await` are very similar to
typical blocking calls, calls to `await` are translated to efficient
non-blocking code under the hood. Example:

    01:  val fut: Future[String] = ...
    02:  
    03:  val messageFut = async {
    04:    // potentially expensive computation
    05:    val user = ...
    06:    // to continue we need the result of `fut`:
    07:    val res = await(fut)
    08:    val (month, day) = res
    09:    s"Hello $user, it's $month!"
    10:  }

In the above example, the evaluation of the body of `async` suspends
at line 7 until the future `fut` is completed. There are several
possible outcomes: either `fut` is completed successfully in which case
the evaluation resumes on line 8 such that `res` is bound to the
successful result. Another possibility is that `fut` is completed
with an exception. In that case, `messageFut` is immediately completed
with the same exception. (A timeout is handled like an unsuccessful
completion with a `TimeoutException`.)

## Illegal Uses of `await`

The `await` construct can be used within most of Scala's standard
control structures such as if-else and match expressions. However,
certain uses of `await` are illegal, since they make it impossible to
generate the state machine of the `async` block. This section explains
where it is illegal to use `await` and what options exist to work
around issues that arise from those restrictions.

The most important limitation of `await` is that it cannot be called
from within closures inside an `async` block. This means an `await`
can only occur within a directly enclosing `async` block. Sometimes
this can be hard to avoid, for example, when calling higher-order
functions on collections. Here is an example:

    async {
      val list = ...

      list map { idx =>
        // need to suspend
        val res = await(someFut(idx))

        transform(res)
      }
    }

The above example will fail to compile since `await` is called inside
the closure passed to `map`. In situations like this it can be useful
to wrap (parts of) the closure's body in a nested `async` block. Of
course, this changes the result type of the `map` call: we're now
dealing with a collection of futures instead of a collection of strict
results. Fortunately, the futures API provides a few utilities for
working with collections of futures. In this case, the `sequence`
combinator, which converts a collection with elements of type `Future[T]` into a future of a
collection of elements of type `T`, is most useful:

    async {
      val list = ...

      val colFuts = list map { idx =>
        async {
          // need to suspend
          val res = await(someFut(idx))

          transform(res)
        }
      }

      await(Future.sequence(colFuts))
    }

Besides closures, there are other points in a program where `await` cannot be used. For example, if the `async` block contains a nested class, trait, or object, it is illegal to call `await` inside of it without an `async` block that directly encloses the `await` call.

Similar to closures, it is illegal to use `await` within an argument of a by-name parameter. In some cases a work-around is to implement the method with the by-name parameter as a macro, so that the method body is inlined. In many cases that's enough to avoid any further issues. However, it is only recommended for advanced users, since the solution relies on using macros.

## Debugging

The Async distribution contains utilities that simplify debugging
`async` blocks. By importing the `async` and `await` members from the
`scala.async.BlockingAsync` object instead of from the
`scala.async.Async` object, a different evaluation strategy for
`async` blocks is selected. The "blocking" strategy ensures that each
`async` block is executed on a single thread in such a way that
execution never leaves that thread. As a result, regular debuggers can
be used to step through the code of an `async` block without losing
the thread's stack. To make this possible, in this mode all calls to
`await` are blocking: the current thread is blocked until the
corresponding future is completed in which case the thread is
unblocked to resume its computation.