- Notifications
You must be signed in to change notification settings - Fork90
An asynchronous programming facility for Scala
License
scala/scala-async
Folders and files
Name | Name | Last commit message | Last commit date | |
---|---|---|---|---|
Repository files navigation
This branch (version series 0.10.x) targets Scala 2.12 and 2.13.
Support for Scala 2.11 ison a branch.
Support for Scala 2.10 ison a branch.
To include scala-async in an existing project use the library published on Maven Central.For sbt projects add the following to your build definition - build.sbt or project/Build.scala:
libraryDependencies+="org.scala-lang.modules"%%"scala-async"%"0.9.7"
For Maven projects add the following to your (make sure to use the correct Scala version suffixto match your project’s Scala binary version):
<dependency><groupId>org.scala-lang.modules</groupId><artifactId>scala-async_2.12</artifactId><version>0.10.0</version></dependency>
After adding a scala-async to your classpath, write your firstasync
block:
importscala.concurrent.ExecutionContext.Implicits.globalimportscala.async.Async.{async,await}valfuture= async {valf1= async { ...;true }valf2= async { ...;42 }if (await(f1)) await(f2)else0}
async
marks a block of asynchronous code. Such a block usually containsone or moreawait
calls, which marks a point at which the computationwill be suspended until the awaitedFuture
is complete.
By default,async
blocks operate onscala.concurrent.{Future, Promise}
.The system can be adapted to alternative implementations of theFuture
pattern.
Consider the following example:
defslowCalcFuture:Future[Int]= ...// 01defcombined:Future[Int]= async {// 02 await(slowCalcFuture)+ await(slowCalcFuture)// 03}valx:Int=Await.result(combined,10.seconds)// 05
Line 1 defines an asynchronous method: it returns aFuture
.
Line 2 begins anasync
block. During compilation,the contents of this block will be analyzed to identifytheawait
calls, and transformed into non-blockingcode.
Control flow will immediately pass to line 5, as thecomputation in theasync
block is not executedon the caller's thread.
Line 3 begins by triggeringslowCalcFuture
, and thensuspending until it has been calculated. Only after ithas finished, we trigger it again, and suspend again.Finally, we add the results and completecombined
, whichin turn will release line 5 (unless it had already timed out).
It is important to note that while lines 1-4 are non-blocking,they are not parallel. If we wanted to parallelize the two computations,we could rearrange the code as follows:
defcombined:Future[Int]= async {valfuture1= slowCalcFuturevalfuture2= slowCalcFuture await(future1)+ await(future2)}
This computation could also be expressed by directly using thehigher-order functions of Futures:
defslowCalcFuture:Future[Int]= ...valfuture1= slowCalcFuturevalfuture2= slowCalcFuturedefcombined:Future[Int]=for { r1<- future1 r2<- future2}yield r1+ r2
Theasync
approach has two advantages over the use ofmap
andflatMap
:
- The code more directly reflects the programmer's intent,and does not require us to name the results
r1
andr2
.This advantage is even more pronounced when we mix controlstructures inasync
blocks. async
blocks are compiled to a single anonymous class,as opposed to a separate anonymous class for each closurerequired at each generator (<-
) in the for-comprehension.This reduces the size of generated code, and can avoid boxingof intermediate results.
The existing continuations (CPS) plugin for Scala can also be usedto provide a syntactic layer likeasync
. This approach has beenused in Akka'sDataflow Concurrency(now deprecated in favour of this library).
CPS-based rewriting of asynchronous code also produces a closurefor each suspension. It can also lead to type errors that aredifficult to understand.
- The
async
macro analyses the block of code, looking for controlstructures and locations ofawait
calls. It then breaks the codeinto 'chunks'. Each chunk contains a linear sequence of statementsthat concludes with a branching decision, or with the registrationof a subsequent state handler as the continuation. - Before this analysis and transformation, the program is normalizedinto a form amenable to this manipulation. This is called the"A Normal Form" (ANF), and roughly means that:
if
andmatch
constructs are only used as statements;they cannot be used as an expression.- calls to
await
are not allowed in compound expressions.
- Identify vals, vars and defs that are accessed from multiplestates. These will be lifted out to fields in the state machineobject.
- Synthesize a class that holds:
- an integer representing the current state ID.
- the lifted definitions.
- an
apply(value: Try[Any]): Unit
method that will becalled on completion of each future. The behavior ofthis method is determined by the current state. It recordsthe downcast result of the future in a field, and calls theresume()
method. - the
resume(): Unit
method that switches on the current stateand runs the users code for one 'chunk', and either:a) registers the state machine as the handler for the next futureb) completes the result Promise of theasync
block, if at the terminal state. - an
apply(): Unit
method that starts the computation.
- See theneg test casesfor constructs that are not allowed in an
async
block. - See theissue list for which of these restrictions are plannedto be dropped in the future.
- See#32 for why
await
is not possible in closures, and for suggestions onways to structure the code to work around this limitation.
About
An asynchronous programming facility for Scala
Topics
Resources
License
Code of conduct
Uh oh!
There was an error while loading.Please reload this page.
Stars
Watchers
Forks
Packages0
Uh oh!
There was an error while loading.Please reload this page.