From 36ef8820b8fa25f8d3756b1b618840179d09b4de Mon Sep 17 00:00:00 2001 From: Viktor Klang Date: Tue, 24 Jan 2012 12:13:13 +0100 Subject: [PATCH 1/4] Adding docs and samples for onSuccess, onFailure, onComplete, filter, or, zip and successful/failed --- .../code/akka/docs/future/FutureDocSpec.scala | 76 +++++++++++++++++++ akka-docs/scala/futures.rst | 57 +++++++++++--- 2 files changed, 123 insertions(+), 10 deletions(-) diff --git a/akka-docs/scala/code/akka/docs/future/FutureDocSpec.scala b/akka-docs/scala/code/akka/docs/future/FutureDocSpec.scala index 69bfe31899..d1e9532548 100644 --- a/akka-docs/scala/code/akka/docs/future/FutureDocSpec.scala +++ b/akka-docs/scala/code/akka/docs/future/FutureDocSpec.scala @@ -125,12 +125,22 @@ class FutureDocSpec extends AkkaSpec { //#flat-map } + "demonstrate usage of filter" in { + //#filter + val future1 = Promise.successful(4) + val future2 = future1.filter(_ % 2 == 0) + val result = Await.result(future2, 1 second) + result must be(4) + //#filter + } + "demonstrate usage of for comprehension" in { //#for-comprehension val f = for { a ← Future(10 / 2) // 10 / 2 = 5 b ← Future(a + 1) // 5 + 1 = 6 c ← Future(a - 1) // 5 - 1 = 4 + if c > 3 // Future.filter } yield b * c // 6 * 4 = 24 // Note that the execution of futures a, b, and c @@ -250,4 +260,70 @@ class FutureDocSpec extends AkkaSpec { Await.result(future, 1 second) must be(0) } + "demonstrate usage of zip" in { + val future1 = Future { "foo" } + val future2 = Future { "bar" } + //#zip + val future3 = future1 zip future2 map { case (a, b) ⇒ a + " " + b } + //#zip + Await.result(future3, 1 second) must be("foo bar") + } + + "demonstrate usage of or" in { + val future1 = Future { "foo" } + val future2 = Future { "bar" } + val future3 = Future { "pigdog" } + //#or + val future4 = future1 or future2 or future3 + //#or + Await.result(future4, 1 second) must be("foo") + } + + "demonstrate usage of onSuccess & onFailure & onComplete" in { + { + val future = Future { "foo" } + //#onSuccess + future onSuccess { + case "bar" ⇒ println("Got my bar alright!") + case x: String ⇒ println("Got some random string: " + x) + } + //#onSuccess + Await.result(future, 1 second) must be("foo") + } + { + val future = Promise.failed[String](new IllegalStateException("OHNOES")) + //#onFailure + future onFailure { + case ise: IllegalStateException if ise.getMessage == "OHNOES" ⇒ + //OHNOES! We are in deep trouble, do something! + case e: Exception ⇒ + //Do something else + } + //#onFailure + } + { + val future = Future { "foo" } + def doSomethingOnSuccess(r: String) = () + def doSomethingOnFailure(t: Throwable) = () + //#onComplete + future onComplete { + case Right(result) ⇒ doSomethingOnSuccess(result) //Right == Success + case Left(failure) ⇒ doSomethingOnFailure(failure) //Left == Failure + } + //#onComplete + Await.result(future, 1 second) must be("foo") + } + } + + "demonstrate usage of Promise.success & Promise.failed" in { + //#successful + val future = Promise.successful("Yay!") + //#successful + //#failed + val otherFuture = Promise.failed[String](new IllegalArgumentException("Bang!")) + //#failed + Await.result(future, 1 second) must be("Yay!") + intercept[IllegalArgumentException] { Await.result(otherFuture, 1 second) } + } + } diff --git a/akka-docs/scala/futures.rst b/akka-docs/scala/futures.rst index 5707fcf029..558995de89 100644 --- a/akka-docs/scala/futures.rst +++ b/akka-docs/scala/futures.rst @@ -42,6 +42,16 @@ A common use case within Akka is to have some computation performed concurrently In the above code the block passed to ``Future`` will be executed by the default ``Dispatcher``, with the return value of the block used to complete the ``Future`` (in this case, the result would be the string: "HelloWorld"). Unlike a ``Future`` that is returned from an ``Actor``, this ``Future`` is properly typed, and we also avoid the overhead of managing an ``Actor``. +You can also create already completed Futures using the ``Promise`` companion, which can be either successes: + +.. includecode:: code/akka/docs/future/FutureDocSpec.scala + :include: successful + +Or failures: + +.. includecode:: code/akka/docs/future/FutureDocSpec.scala + :include: failed + Functional Futures ------------------ @@ -67,10 +77,15 @@ The ``map`` method is fine if we are modifying a single ``Future``, but if 2 or .. includecode:: code/akka/docs/future/FutureDocSpec.scala :include: flat-map +If you need to do conditional propagation, you can use ``filter``: + +.. includecode:: code/akka/docs/future/FutureDocSpec.scala + :include: filter + For Comprehensions ^^^^^^^^^^^^^^^^^^ -Since ``Future`` has a ``map`` and ``flatMap`` method it can be easily used in a 'for comprehension': +Since ``Future`` has a ``map``, ``filter` and ``flatMap`` method it can be easily used in a 'for comprehension': .. includecode:: code/akka/docs/future/FutureDocSpec.scala :include: for-comprehension @@ -111,7 +126,9 @@ This is the same result as this example: But it may be faster to use ``traverse`` as it doesn't have to create an intermediate ``List[Future[Int]]``. -Then there's a method that's called ``fold`` that takes a start-value, a sequence of ``Future``\s and a function from the type of the start-value and the type of the futures and returns something with the same type as the start-value, and then applies the function to all elements in the sequence of futures, non-blockingly, the execution will run on the Thread of the last completing Future in the sequence. +Then there's a method that's called ``fold`` that takes a start-value, a sequence of ``Future``\s and a function +from the type of the start-value and the type of the futures and returns something with the same type as the start-value, +and then applies the function to all elements in the sequence of futures, non-blockingly, the execution will run on the Thread of the last completing Future in the sequence. .. includecode:: code/akka/docs/future/FutureDocSpec.scala :include: fold @@ -119,24 +136,44 @@ Then there's a method that's called ``fold`` that takes a start-value, a sequenc That's all it takes! -If the sequence passed to ``fold`` is empty, it will return the start-value, in the case above, that will be 0. In some cases you don't have a start-value and you're able to use the value of the first completing Future in the sequence as the start-value, you can use ``reduce``, it works like this: +If the sequence passed to ``fold`` is empty, it will return the start-value, in the case above, that will be 0. +In some cases you don't have a start-value and you're able to use the value of the first completing Future in the sequence as the start-value, you can use ``reduce``, it works like this: .. includecode:: code/akka/docs/future/FutureDocSpec.scala :include: reduce -Same as with ``fold``, the execution will be done by the Thread that completes the last of the Futures, you can also parallelize it by chunking your futures into sub-sequences and reduce them, and then reduce the reduced results again. +Same as with ``fold``, the execution will be done by the Thread that completes the last of the Futures, ` +you can also parallelize it by chunking your futures into sub-sequences and reduce them, and then reduce the reduced results again. -This is just a sample of what can be done, but to use more advanced techniques it is easier to take advantage of Scalaz. +Callbacks +--------- +Sometimes you just want to listen to a ``Future`` being completed, and react to that not by creating a new Future, but by side-effecting. +For this Akka supports ``onComplete``, ``onSuccess`` and ``onFailure``, of which the latter two are specializations of the first. -Scalaz -^^^^^^ +.. includecode:: code/akka/docs/future/FutureDocSpec.scala + :include: onSuccess -There is also an `Akka-Scalaz`_ project created by Derek Williams for a more -complete support of programming in a functional style. +.. includecode:: code/akka/docs/future/FutureDocSpec.scala + :include: onFailure -.. _Akka-Scalaz: https://github.com/derekjw/akka-scalaz +.. includecode:: code/akka/docs/future/FutureDocSpec.scala + :include: onComplete +Auxiliary methods +----------------- + +``Future`` ``or`` combines 2 Futures into a new ``Future``, and will hold the successful value of the second ``Future`` +if the first ``Future`` fails. + +.. includecode:: code/akka/docs/future/FutureDocSpec.scala + :include: or + +You can also combine two Futures into a new ``Future`` that will hold a tuple of the two Futures successful results, +using the ``zip`` operation. + +.. includecode:: code/akka/docs/future/FutureDocSpec.scala + :include: zip Exceptions ---------- From c351aef39cc68fdc8cd481b58f34d60deddfca1d Mon Sep 17 00:00:00 2001 From: Viktor Klang Date: Tue, 24 Jan 2012 13:59:23 +0100 Subject: [PATCH 2/4] Correcting spelling errors and fixing formatting of futures.rst --- akka-docs/scala/futures.rst | 95 +++++++++++++++++++++++++++---------- 1 file changed, 70 insertions(+), 25 deletions(-) diff --git a/akka-docs/scala/futures.rst b/akka-docs/scala/futures.rst index 558995de89..5d0baa5ca7 100644 --- a/akka-docs/scala/futures.rst +++ b/akka-docs/scala/futures.rst @@ -10,37 +10,48 @@ Futures (Scala) Introduction ------------ -In Akka, a `Future `_ is a data structure used to retrieve the result of some concurrent operation. This operation is usually performed by an ``Actor`` or by the ``Dispatcher`` directly. This result can be accessed synchronously (blocking) or asynchronously (non-blocking). +In Akka, a `Future `_ is a data structure used to +retrieve the result of some concurrent operation. This operation is usually performed by an ``Actor`` +or by the ``Dispatcher`` directly. This result can be accessed synchronously (blocking) or asynchronously (non-blocking). Use with Actors --------------- -There are generally two ways of getting a reply from an ``Actor``: the first is by a sent message (``actor ! msg``), which only works if the original sender was an ``Actor``) and the second is through a ``Future``. +There are generally two ways of getting a reply from an ``Actor``: the first is by a sent message (``actor ! msg``), +which only works if the original sender was an ``Actor``) and the second is through a ``Future``. Using an ``Actor``\'s ``?`` method to send a message will return a Future. To wait for and retrieve the actual result the simplest method is: .. includecode:: code/akka/docs/future/FutureDocSpec.scala :include: ask-blocking -This will cause the current thread to block and wait for the ``Actor`` to 'complete' the ``Future`` with it's reply. Blocking is discouraged though as it can cause performance problem. -The blocking operations are located in ``Await.result`` and ``Await.ready`` to make it easy to spot where blocking occurs. Alternatives to blocking are discussed further within this documentation. -Also note that the ``Future`` returned by an ``Actor`` is a ``Future[Any]`` since an ``Actor`` is dynamic. That is why the ``asInstanceOf`` is used in the above sample. +This will cause the current thread to block and wait for the ``Actor`` to 'complete' the ``Future`` with it's reply. +Blocking is discouraged though as it will cause performance problems. +The blocking operations are located in ``Await.result`` and ``Await.ready`` to make it easy to spot where blocking occurs. +Alternatives to blocking are discussed further within this documentation. Also note that the ``Future`` returned by +an ``Actor`` is a ``Future[Any]`` since an ``Actor`` is dynamic. That is why the ``asInstanceOf`` is used in the above sample. When using non-blocking it is better to use the ``mapTo`` method to safely try to cast a ``Future`` to an expected type: .. includecode:: code/akka/docs/future/FutureDocSpec.scala :include: map-to -The ``mapTo`` method will return a new ``Future`` that contains the result if the cast was successful, or a ``ClassCastException`` if not. Handling ``Exception``\s will be discussed further within this documentation. +The ``mapTo`` method will return a new ``Future`` that contains the result if the cast was successful, +or a ``ClassCastException`` if not. Handling ``Exception``\s will be discussed further within this documentation. Use Directly ------------ -A common use case within Akka is to have some computation performed concurrently without needing the extra utility of an ``Actor``. If you find yourself creating a pool of ``Actor``\s for the sole reason of performing a calculation in parallel, there is an easier (and faster) way: +A common use case within Akka is to have some computation performed concurrently without needing the extra utility of an ``Actor``. +If you find yourself creating a pool of ``Actor``\s for the sole reason of performing a calculation in parallel, +there is an easier (and faster) way: .. includecode:: code/akka/docs/future/FutureDocSpec.scala :include: future-eval -In the above code the block passed to ``Future`` will be executed by the default ``Dispatcher``, with the return value of the block used to complete the ``Future`` (in this case, the result would be the string: "HelloWorld"). Unlike a ``Future`` that is returned from an ``Actor``, this ``Future`` is properly typed, and we also avoid the overhead of managing an ``Actor``. +In the above code the block passed to ``Future`` will be executed by the default ``Dispatcher``, +with the return value of the block used to complete the ``Future`` (in this case, the result would be the string: "HelloWorld"). +Unlike a ``Future`` that is returned from an ``Actor``, this ``Future`` is properly typed, +and we also avoid the overhead of managing an ``Actor``. You can also create already completed Futures using the ``Promise`` companion, which can be either successes: @@ -55,19 +66,28 @@ Or failures: Functional Futures ------------------ -Akka's ``Future`` has several monadic methods that are very similar to the ones used by Scala's collections. These allow you to create 'pipelines' or 'streams' that the result will travel through. +Akka's ``Future`` has several monadic methods that are very similar to the ones used by Scala's collections. +These allow you to create 'pipelines' or 'streams' that the result will travel through. Future is a Monad ^^^^^^^^^^^^^^^^^ -The first method for working with ``Future`` functionally is ``map``. This method takes a ``Function`` which performs some operation on the result of the ``Future``, and returning a new result. The return value of the ``map`` method is another ``Future`` that will contain the new result: +The first method for working with ``Future`` functionally is ``map``. This method takes a ``Function`` +which performs some operation on the result of the ``Future``, and returning a new result. +The return value of the ``map`` method is another ``Future`` that will contain the new result: .. includecode:: code/akka/docs/future/FutureDocSpec.scala :include: map -In this example we are joining two strings together within a ``Future``. Instead of waiting for this to complete, we apply our function that calculates the length of the string using the ``map`` method. Now we have a second ``Future`` that will eventually contain an ``Int``. When our original ``Future`` completes, it will also apply our function and complete the second ``Future`` with it's result. When we finally get the result, it will contain the number 10. Our original ``Future`` still contains the string "HelloWorld" and is unaffected by the ``map``. +In this example we are joining two strings together within a ``Future``. Instead of waiting for this to complete, +we apply our function that calculates the length of the string using the ``map`` method. +Now we have a second ``Future`` that will eventually contain an ``Int``. +When our original ``Future`` completes, it will also apply our function and complete the second ``Future`` with its result. +When we finally get the result, it will contain the number 10. Our original ``Future`` still contains the +string "HelloWorld" and is unaffected by the ``map``. -The ``map`` method is fine if we are modifying a single ``Future``, but if 2 or more ``Future``\s are involved ``map`` will not allow you to combine them together: +The ``map`` method is fine if we are modifying a single ``Future``, +but if 2 or more ``Future``\s are involved ``map`` will not allow you to combine them together: .. includecode:: code/akka/docs/future/FutureDocSpec.scala :include: wrong-nested-map @@ -90,31 +110,47 @@ Since ``Future`` has a ``map``, ``filter` and ``flatMap`` method it can be easil .. includecode:: code/akka/docs/future/FutureDocSpec.scala :include: for-comprehension -Something to keep in mind when doing this is even though it looks like parts of the above example can run in parallel, each step of the for comprehension is run sequentially. This will happen on separate threads for each step but there isn't much benefit over running the calculations all within a single ``Future``. The real benefit comes when the ``Future``\s are created first, and then combining them together. +Something to keep in mind when doing this is even though it looks like parts of the above example can run in parallel, +each step of the for comprehension is run sequentially. This will happen on separate threads for each step but +there isn't much benefit over running the calculations all within a single ``Future``. +The real benefit comes when the ``Future``\s are created first, and then combining them together. Composing Futures ^^^^^^^^^^^^^^^^^ -The example for comprehension above is an example of composing ``Future``\s. A common use case for this is combining the replies of several ``Actor``\s into a single calculation without resorting to calling ``Await.result`` or ``Await.ready`` to block for each result. First an example of using ``Await.result``: +The example for comprehension above is an example of composing ``Future``\s. +A common use case for this is combining the replies of several ``Actor``\s into a single calculation +without resorting to calling ``Await.result`` or ``Await.ready`` to block for each result. +First an example of using ``Await.result``: .. includecode:: code/akka/docs/future/FutureDocSpec.scala :include: composing-wrong -Here we wait for the results from the first 2 ``Actor``\s before sending that result to the third ``Actor``. We called ``Await.result`` 3 times, which caused our little program to block 3 times before getting our final result. Now compare that to this example: +Here we wait for the results from the first 2 ``Actor``\s before sending that result to the third ``Actor``. +We called ``Await.result`` 3 times, which caused our little program to block 3 times before getting our final result. +Now compare that to this example: .. includecode:: code/akka/docs/future/FutureDocSpec.scala :include: composing -Here we have 2 actors processing a single message each. Once the 2 results are available (note that we don't block to get these results!), they are being added together and sent to a third ``Actor``, which replies with a string, which we assign to 'result'. +Here we have 2 actors processing a single message each. Once the 2 results are available +(note that we don't block to get these results!), they are being added together and sent to a third ``Actor``, +which replies with a string, which we assign to 'result'. -This is fine when dealing with a known amount of Actors, but can grow unwieldy if we have more then a handful. The ``sequence`` and ``traverse`` helper methods can make it easier to handle more complex use cases. Both of these methods are ways of turning, for a subclass ``T`` of ``Traversable``, ``T[Future[A]]`` into a ``Future[T[A]]``. For example: +This is fine when dealing with a known amount of Actors, but can grow unwieldy if we have more then a handful. +The ``sequence`` and ``traverse`` helper methods can make it easier to handle more complex use cases. +Both of these methods are ways of turning, for a subclass ``T`` of ``Traversable``, ``T[Future[A]]`` into a ``Future[T[A]]``. +For example: .. includecode:: code/akka/docs/future/FutureDocSpec.scala :include: sequence-ask -To better explain what happened in the example, ``Future.sequence`` is taking the ``List[Future[Int]]`` and turning it into a ``Future[List[Int]]``. We can then use ``map`` to work with the ``List[Int]`` directly, and we find the sum of the ``List``. +To better explain what happened in the example, ``Future.sequence`` is taking the ``List[Future[Int]]`` +and turning it into a ``Future[List[Int]]``. We can then use ``map`` to work with the ``List[Int]`` directly, a +nd we find the sum of the ``List``. -The ``traverse`` method is similar to ``sequence``, but it takes a ``T[A]`` and a function ``A => Future[B]`` to return a ``Future[T[B]]``, where ``T`` is again a subclass of Traversable. For example, to use ``traverse`` to sum the first 100 odd numbers: +The ``traverse`` method is similar to ``sequence``, but it takes a ``T[A]`` and a function ``A => Future[B]`` to return a ``Future[T[B]]``, +where ``T`` is again a subclass of Traversable. For example, to use ``traverse`` to sum the first 100 odd numbers: .. includecode:: code/akka/docs/future/FutureDocSpec.scala :include: traverse @@ -128,7 +164,8 @@ But it may be faster to use ``traverse`` as it doesn't have to create an interme Then there's a method that's called ``fold`` that takes a start-value, a sequence of ``Future``\s and a function from the type of the start-value and the type of the futures and returns something with the same type as the start-value, -and then applies the function to all elements in the sequence of futures, non-blockingly, the execution will run on the Thread of the last completing Future in the sequence. +and then applies the function to all elements in the sequence of futures, non-blockingly, +the execution will run on the Thread of the last completing Future in the sequence. .. includecode:: code/akka/docs/future/FutureDocSpec.scala :include: fold @@ -137,7 +174,8 @@ That's all it takes! If the sequence passed to ``fold`` is empty, it will return the start-value, in the case above, that will be 0. -In some cases you don't have a start-value and you're able to use the value of the first completing Future in the sequence as the start-value, you can use ``reduce``, it works like this: +In some cases you don't have a start-value and you're able to use the value of the first completing Future in the sequence +as the start-value, you can use ``reduce``, it works like this: .. includecode:: code/akka/docs/future/FutureDocSpec.scala :include: reduce @@ -163,7 +201,7 @@ For this Akka supports ``onComplete``, ``onSuccess`` and ``onFailure``, of which Auxiliary methods ----------------- -``Future`` ``or`` combines 2 Futures into a new ``Future``, and will hold the successful value of the second ``Future`` +``Future`` ``or`` combines 2 Futures into a new ``Future``, and will hold the successful value of the second ``Future` if the first ``Future`` fails. .. includecode:: code/akka/docs/future/FutureDocSpec.scala @@ -178,12 +216,19 @@ using the ``zip`` operation. Exceptions ---------- -Since the result of a ``Future`` is created concurrently to the rest of the program, exceptions must be handled differently. It doesn't matter if an ``Actor`` or the dispatcher is completing the ``Future``, if an ``Exception`` is caught the ``Future`` will contain it instead of a valid result. If a ``Future`` does contain an ``Exception``, calling ``Await.result`` will cause it to be thrown again so it can be handled properly. +Since the result of a ``Future`` is created concurrently to the rest of the program, exceptions must be handled differently. +It doesn't matter if an ``Actor`` or the dispatcher is completing the ``Future``, +if an ``Exception`` is caught the ``Future`` will contain it instead of a valid result. +If a ``Future`` does contain an ``Exception``, calling ``Await.result`` will cause it to be thrown again so it can be handled properly. -It is also possible to handle an ``Exception`` by returning a different result. This is done with the ``recover`` method. For example: +It is also possible to handle an ``Exception`` by returning a different result. +This is done with the ``recover`` method. For example: .. includecode:: code/akka/docs/future/FutureDocSpec.scala :include: recover -In this example, if the actor replied with a ``akka.actor.Status.Failure`` containing the ``ArithmeticException``, our ``Future`` would have a result of 0. The ``recover`` method works very similarly to the standard try/catch blocks, so multiple ``Exception``\s can be handled in this manner, and if an ``Exception`` is not handled this way it will be behave as if we hadn't used the ``recover`` method. +In this example, if the actor replied with a ``akka.actor.Status.Failure`` containing the ``ArithmeticException``, +our ``Future`` would have a result of 0. The ``recover`` method works very similarly to the standard try/catch blocks, +so multiple ``Exception``\s can be handled in this manner, and if an ``Exception`` is not handled this way +it will behave as if we hadn't used the ``recover`` method. From 28dfdaba32b6b769f0ef52389793601d2ed843c9 Mon Sep 17 00:00:00 2001 From: Viktor Klang Date: Tue, 24 Jan 2012 15:03:32 +0100 Subject: [PATCH 3/4] Correcting after review --- .../scala/code/akka/docs/future/FutureDocSpec.scala | 10 ++++++++-- akka-docs/scala/futures.rst | 6 +++--- 2 files changed, 11 insertions(+), 5 deletions(-) diff --git a/akka-docs/scala/code/akka/docs/future/FutureDocSpec.scala b/akka-docs/scala/code/akka/docs/future/FutureDocSpec.scala index d1e9532548..175fc08ff5 100644 --- a/akka-docs/scala/code/akka/docs/future/FutureDocSpec.scala +++ b/akka-docs/scala/code/akka/docs/future/FutureDocSpec.scala @@ -131,6 +131,12 @@ class FutureDocSpec extends AkkaSpec { val future2 = future1.filter(_ % 2 == 0) val result = Await.result(future2, 1 second) result must be(4) + + val failedFilter = future1.filter(_ % 2 == 1).recover { + case m: MatchError ⇒ 0 //When filter fails, it will have a MatchError + } + val result2 = Await.result(failedFilter, 1 second) + result2 must be(0) //Can only be 0 when there was a MatchError //#filter } @@ -307,8 +313,8 @@ class FutureDocSpec extends AkkaSpec { def doSomethingOnFailure(t: Throwable) = () //#onComplete future onComplete { - case Right(result) ⇒ doSomethingOnSuccess(result) //Right == Success - case Left(failure) ⇒ doSomethingOnFailure(failure) //Left == Failure + case Right(result) ⇒ doSomethingOnSuccess(result) + case Left(failure) ⇒ doSomethingOnFailure(failure) } //#onComplete Await.result(future, 1 second) must be("foo") diff --git a/akka-docs/scala/futures.rst b/akka-docs/scala/futures.rst index 5d0baa5ca7..c46db30927 100644 --- a/akka-docs/scala/futures.rst +++ b/akka-docs/scala/futures.rst @@ -164,8 +164,8 @@ But it may be faster to use ``traverse`` as it doesn't have to create an interme Then there's a method that's called ``fold`` that takes a start-value, a sequence of ``Future``\s and a function from the type of the start-value and the type of the futures and returns something with the same type as the start-value, -and then applies the function to all elements in the sequence of futures, non-blockingly, -the execution will run on the Thread of the last completing Future in the sequence. +and then applies the function to all elements in the sequence of futures, asynchronously, +the execution will start when the last of the Futures is completed. .. includecode:: code/akka/docs/future/FutureDocSpec.scala :include: fold @@ -180,7 +180,7 @@ as the start-value, you can use ``reduce``, it works like this: .. includecode:: code/akka/docs/future/FutureDocSpec.scala :include: reduce -Same as with ``fold``, the execution will be done by the Thread that completes the last of the Futures, ` +Same as with ``fold``, the execution will be done asynchronously when the last of the Future is completed,` you can also parallelize it by chunking your futures into sub-sequences and reduce them, and then reduce the reduced results again. Callbacks From 5483dacabaff767384a01b3a71db048917afb491 Mon Sep 17 00:00:00 2001 From: Viktor Klang Date: Tue, 24 Jan 2012 16:31:20 +0100 Subject: [PATCH 4/4] Adding Future docs for Java API --- .../akka/docs/future/FutureDocTestBase.java | 177 +++++++++++++++--- akka-docs/java/futures.rst | 134 ++++++++++--- 2 files changed, 259 insertions(+), 52 deletions(-) diff --git a/akka-docs/java/code/akka/docs/future/FutureDocTestBase.java b/akka-docs/java/code/akka/docs/future/FutureDocTestBase.java index d2adfaf5fa..e642047709 100644 --- a/akka-docs/java/code/akka/docs/future/FutureDocTestBase.java +++ b/akka-docs/java/code/akka/docs/future/FutureDocTestBase.java @@ -4,6 +4,9 @@ package akka.docs.future; //#imports1 +import akka.dispatch.Promise; +import akka.japi.Procedure; +import akka.japi.Procedure2; import akka.util.Timeout; import akka.dispatch.Await; import akka.dispatch.Future; @@ -196,17 +199,19 @@ public class FutureDocTestBase { Iterable> listOfFutureInts = source; // now we have a Future[Iterable[Integer]] - Future> futureListOfInts = sequence(listOfFutureInts, system.dispatcher()); + Future> futureListOfInts = + sequence(listOfFutureInts, system.dispatcher()); // Find the sum of the odd numbers - Future futureSum = futureListOfInts.map(new Function, Long>() { - public Long apply(Iterable ints) { - long sum = 0; - for (Integer i : ints) - sum += i; - return sum; - } - }); + Future futureSum = futureListOfInts.map( + new Function, Long>() { + public Long apply(Iterable ints) { + long sum = 0; + for (Integer i : ints) + sum += i; + return sum; + } + }); long result = Await.result(futureSum, Duration.create(1, SECONDS)); //#sequence @@ -219,18 +224,20 @@ public class FutureDocTestBase { //Just a sequence of Strings Iterable listStrings = Arrays.asList("a", "b", "c"); - Future> futureResult = traverse(listStrings, new Function>() { - public Future apply(final String r) { - return future(new Callable() { - public String call() { - return r.toUpperCase(); - } - }, system.dispatcher()); - } - }, system.dispatcher()); + Future> futureResult = traverse(listStrings, + new Function>() { + public Future apply(final String r) { + return future(new Callable() { + public String call() { + return r.toUpperCase(); + } + }, system.dispatcher()); + } + }, system.dispatcher()); //Returns the sequence of strings as upper case - Iterable result = Await.result(futureResult, Duration.create(1, SECONDS)); + Iterable result = + Await.result(futureResult, Duration.create(1, SECONDS)); assertEquals(Arrays.asList("A", "B", "C"), result); //#traverse } @@ -246,11 +253,12 @@ public class FutureDocTestBase { Iterable> futures = source; //Start value is the empty string - Future resultFuture = fold("", futures, new Function2() { - public String apply(String r, String t) { - return r + t; //Just concatenate - } - }, system.dispatcher()); + Future resultFuture = fold("", futures, + new Function2() { + public String apply(String r, String t) { + return r + t; //Just concatenate + } + }, system.dispatcher()); String result = Await.result(resultFuture, Duration.create(1, SECONDS)); //#fold @@ -267,11 +275,12 @@ public class FutureDocTestBase { //A sequence of Futures, in this case Strings Iterable> futures = source; - Future resultFuture = reduce(futures, new Function2() { - public Object apply(Object r, String t) { - return r + t; //Just concatenate - } - }, system.dispatcher()); + Future resultFuture = reduce(futures, + new Function2() { + public Object apply(Object r, String t) { + return r + t; //Just concatenate + } + }, system.dispatcher()); Object result = Await.result(resultFuture, Duration.create(1, SECONDS)); //#reduce @@ -279,6 +288,116 @@ public class FutureDocTestBase { assertEquals("ab", result); } + @Test public void useSuccessfulAndFailed() { + //#successful + Future future = Futures.successful("Yay!", system.dispatcher()); + //#successful + //#failed + Future otherFuture = + Futures.failed(new IllegalArgumentException("Bang!"), system.dispatcher()); + //#failed + Object result = Await.result(future, Duration.create(1, SECONDS)); + assertEquals("Yay!",result); + Throwable result2 = Await.result(otherFuture.failed(), Duration.create(1, SECONDS)); + assertEquals("Bang!",result2.getMessage()); + } + + @Test public void useFilter() { + //#filter + Future future1 = Futures.successful(4, system.dispatcher()); + Future successfulFilter = + future1.filter(new Function() { + public Boolean apply(Integer i) { return i % 2 == 0; } + }); + + Future failedFilter = + future1.filter(new Function() { + public Boolean apply(Integer i) { return i % 2 != 0; } + }); + //When filter fails, the returned Future will be failed with a scala.MatchError + //#filter + } + + @Test public void useOnSuccessOnFailureAndOnComplete() { + { + Future future = Futures.successful("foo", system.dispatcher()); + //#onSuccess + future.onSuccess(new Procedure() { + public void apply(String result) { + if ("bar" == result) { + //Do something if it resulted in "bar" + } else { + //Do something if it was some other String + } + } + }); + //#onSuccess + } + { + Future future = + Futures.failed(new IllegalStateException("OHNOES"), system.dispatcher()); + //#onFailure + future.onFailure( new Procedure() { + public void apply(Throwable failure) { + if (failure instanceof IllegalStateException) { + //Do something if it was this particular failure + } else { + //Do something if it was some other failure + } + } + }); + //#onFailure + } + { + Future future = Futures.successful("foo", system.dispatcher()); + //#onComplete + future.onComplete(new Procedure2() { + public void apply(Throwable failure, String result) { + if (failure != null) { + //We got a failure, handle it here + } else { + // We got a result, do something with it + } + } + }); + //#onComplete + } + } + + @Test public void useOrAndZip(){ + { + //#zip + Future future1 = Futures.successful("foo", system.dispatcher()); + Future future2 = Futures.successful("bar", system.dispatcher()); + Future future3 = + future1.zip(future2).map(new Function, String>() { + public String apply(scala.Tuple2 zipped) { + return zipped._1() + " " + zipped._2(); + } + }); + + String result = Await.result(future3, Duration.create(1, SECONDS)); + assertEquals("foo bar", result); + //#zip + } + + { + //#or + Future future1 = + Futures.failed(new IllegalStateException("OHNOES1"), system.dispatcher()); + Future future2 = + Futures.failed(new IllegalStateException("OHNOES2"), system.dispatcher()); + Future future3 = + Futures.successful("bar", system.dispatcher()); + Future future4 = + future1.or(future2).or(future3); // Will have "bar" in this case + String result = Await.result(future4, Duration.create(1, SECONDS)); + assertEquals("bar", result); + //#or + } + + } + public static class MyActor extends UntypedActor { public void onReceive(Object message) { if (message instanceof String) { diff --git a/akka-docs/java/futures.rst b/akka-docs/java/futures.rst index 3a482ab2dc..e9b743535a 100644 --- a/akka-docs/java/futures.rst +++ b/akka-docs/java/futures.rst @@ -10,86 +10,138 @@ Futures (Java) Introduction ------------ -In Akka, a `Future `_ is a data structure used to retrieve the result of some concurrent operation. This operation is usually performed by an ``Actor`` or by the ``Dispatcher`` directly. This result can be accessed synchronously (blocking) or asynchronously (non-blocking). +In Akka, a `Future `_ is a data structure used +to retrieve the result of some concurrent operation. This operation is usually performed by an ``Actor`` or +by the ``Dispatcher`` directly. This result can be accessed synchronously (blocking) or asynchronously (non-blocking). Use with Actors --------------- -There are generally two ways of getting a reply from an ``UntypedActor``: the first is by a sent message (``actorRef.tell(msg)``), which only works if the original sender was an ``UntypedActor``) and the second is through a ``Future``. +There are generally two ways of getting a reply from an ``UntypedActor``: the first is by a sent message (``actorRef.tell(msg)``), +which only works if the original sender was an ``UntypedActor``) and the second is through a ``Future``. -Using the ``ActorRef``\'s ``ask`` method to send a message will return a Future. To wait for and retrieve the actual result the simplest method is: +Using the ``ActorRef``\'s ``ask`` method to send a message will return a Future. +To wait for and retrieve the actual result the simplest method is: .. includecode:: code/akka/docs/future/FutureDocTestBase.java :include: imports1,ask-blocking -This will cause the current thread to block and wait for the ``UntypedActor`` to 'complete' the ``Future`` with it's reply. Blocking is discouraged though as it can cause performance problem. -The blocking operations are located in ``Await.result`` and ``Await.ready`` to make it easy to spot where blocking occurs. Alternatives to blocking are discussed further within this documentation. -Also note that the ``Future`` returned by an ``UntypedActor`` is a ``Future`` since an ``UntypedActor`` is dynamic. That is why the cast to ``String`` is used in the above sample. +This will cause the current thread to block and wait for the ``UntypedActor`` to 'complete' the ``Future`` with it's reply. +Blocking is discouraged though as it can cause performance problem. +The blocking operations are located in ``Await.result`` and ``Await.ready`` to make it easy to spot where blocking occurs. +Alternatives to blocking are discussed further within this documentation. +Also note that the ``Future`` returned by an ``UntypedActor`` is a ``Future`` since an ``UntypedActor`` is dynamic. +That is why the cast to ``String`` is used in the above sample. Use Directly ------------ -A common use case within Akka is to have some computation performed concurrently without needing the extra utility of an ``UntypedActor``. If you find yourself creating a pool of ``UntypedActor``\s for the sole reason of performing a calculation in parallel, there is an easier (and faster) way: +A common use case within Akka is to have some computation performed concurrently without needing +the extra utility of an ``UntypedActor``. If you find yourself creating a pool of ``UntypedActor``\s for the sole reason +of performing a calculation in parallel, there is an easier (and faster) way: .. includecode:: code/akka/docs/future/FutureDocTestBase.java :include: imports2,future-eval -In the above code the block passed to ``future`` will be executed by the default ``Dispatcher``, with the return value of the block used to complete the ``Future`` (in this case, the result would be the string: "HelloWorld"). Unlike a ``Future`` that is returned from an ``UntypedActor``, this ``Future`` is properly typed, and we also avoid the overhead of managing an ``UntypedActor``. +In the above code the block passed to ``future`` will be executed by the default ``Dispatcher``, +with the return value of the block used to complete the ``Future`` (in this case, the result would be the string: "HelloWorld"). +Unlike a ``Future`` that is returned from an ``UntypedActor``, this ``Future`` is properly typed, +and we also avoid the overhead of managing an ``UntypedActor``. + +You can also create already completed Futures using the ``Futures`` class, which can be either successes: + +.. includecode:: code/akka/docs/future/FutureDocTestBase.java + :include: successful + +Or failures: + +.. includecode:: code/akka/docs/future/FutureDocTestBase.java + :include: failed Functional Futures ------------------ -Akka's ``Future`` has several monadic methods that are very similar to the ones used by ``Scala``'s collections. These allow you to create 'pipelines' or 'streams' that the result will travel through. +Akka's ``Future`` has several monadic methods that are very similar to the ones used by ``Scala``'s collections. +These allow you to create 'pipelines' or 'streams' that the result will travel through. Future is a Monad ^^^^^^^^^^^^^^^^^ -The first method for working with ``Future`` functionally is ``map``. This method takes a ``Function`` which performs some operation on the result of the ``Future``, and returning a new result. The return value of the ``map`` method is another ``Future`` that will contain the new result: +The first method for working with ``Future`` functionally is ``map``. This method takes a ``Function`` which performs +some operation on the result of the ``Future``, and returning a new result. +The return value of the ``map`` method is another ``Future`` that will contain the new result: .. includecode:: code/akka/docs/future/FutureDocTestBase.java :include: imports2,map -In this example we are joining two strings together within a Future. Instead of waiting for f1 to complete, we apply our function that calculates the length of the string using the ``map`` method. Now we have a second Future, f2, that will eventually contain an ``Integer``. When our original ``Future``, f1, completes, it will also apply our function and complete the second Future with it's result. When we finally ``get`` the result, it will contain the number 10. Our original Future still contains the string "HelloWorld" and is unaffected by the ``map``. +In this example we are joining two strings together within a Future. Instead of waiting for f1 to complete, +we apply our function that calculates the length of the string using the ``map`` method. +Now we have a second Future, f2, that will eventually contain an ``Integer``. +When our original ``Future``, f1, completes, it will also apply our function and complete the second Future +with its result. When we finally ``get`` the result, it will contain the number 10. +Our original Future still contains the string "HelloWorld" and is unaffected by the ``map``. -Something to note when using these methods: if the ``Future`` is still being processed when one of these methods are called, it will be the completing thread that actually does the work. If the ``Future`` is already complete though, it will be run in our current thread. For example: +Something to note when using these methods: if the ``Future`` is still being processed when one of these methods are called, +it will be the completing thread that actually does the work. +If the ``Future`` is already complete though, it will be run in our current thread. For example: .. includecode:: code/akka/docs/future/FutureDocTestBase.java :include: map2 -The original ``Future`` will take at least 0.1 second to execute now, which means it is still being processed at the time we call ``map``. The function we provide gets stored within the ``Future`` and later executed automatically by the dispatcher when the result is ready. +The original ``Future`` will take at least 0.1 second to execute now, which means it is still being processed at +the time we call ``map``. The function we provide gets stored within the ``Future`` and later executed automatically +by the dispatcher when the result is ready. If we do the opposite: .. includecode:: code/akka/docs/future/FutureDocTestBase.java :include: map3 -Our little string has been processed long before our 0.1 second sleep has finished. Because of this, the dispatcher has moved onto other messages that need processing and can no longer calculate the length of the string for us, instead it gets calculated in the current thread just as if we weren't using a ``Future``. +Our little string has been processed long before our 0.1 second sleep has finished. Because of this, +the dispatcher has moved onto other messages that need processing and can no longer calculate +the length of the string for us, instead it gets calculated in the current thread just as if we weren't using a ``Future``. -Normally this works quite well as it means there is very little overhead to running a quick function. If there is a possibility of the function taking a non-trivial amount of time to process it might be better to have this done concurrently, and for that we use ``flatMap``: +Normally this works quite well as it means there is very little overhead to running a quick function. +If there is a possibility of the function taking a non-trivial amount of time to process it might be better +to have this done concurrently, and for that we use ``flatMap``: .. includecode:: code/akka/docs/future/FutureDocTestBase.java :include: flat-map -Now our second Future is executed concurrently as well. This technique can also be used to combine the results of several Futures into a single calculation, which will be better explained in the following sections. +Now our second Future is executed concurrently as well. This technique can also be used to combine the results +of several Futures into a single calculation, which will be better explained in the following sections. + +If you need to do conditional propagation, you can use ``filter``: + +.. includecode:: code/akka/docs/future/FutureDocTestBase.java + :include: filter Composing Futures ^^^^^^^^^^^^^^^^^ -It is very often desirable to be able to combine different Futures with eachother, below are some examples on how that can be done in a non-blocking fashion. +It is very often desirable to be able to combine different Futures with each other, +below are some examples on how that can be done in a non-blocking fashion. .. includecode:: code/akka/docs/future/FutureDocTestBase.java :include: imports3,sequence -To better explain what happened in the example, ``Future.sequence`` is taking the ``Iterable>`` and turning it into a ``Future>``. We can then use ``map`` to work with the ``Iterable`` directly, and we aggregate the sum of the ``Iterable``. +To better explain what happened in the example, ``Future.sequence`` is taking the ``Iterable>`` +and turning it into a ``Future>``. We can then use ``map`` to work with the ``Iterable`` directly, +and we aggregate the sum of the ``Iterable``. -The ``traverse`` method is similar to ``sequence``, but it takes a sequence of ``A``s and applies a function from ``A`` to ``Future`` and returns a ``Future>``, enabling parallel ``map`` over the sequence, if you use ``Futures.future`` to create the ``Future``. +The ``traverse`` method is similar to ``sequence``, but it takes a sequence of ``A``s and applies a function from ``A`` to ``Future`` +and returns a ``Future>``, enabling parallel ``map`` over the sequence, if you use ``Futures.future`` to create the ``Future``. .. includecode:: code/akka/docs/future/FutureDocTestBase.java :include: imports4,traverse It's as simple as that! -Then there's a method that's called ``fold`` that takes a start-value, a sequence of ``Future``:s and a function from the type of the start-value, a timeout, and the type of the futures and returns something with the same type as the start-value, and then applies the function to all elements in the sequence of futures, non-blockingly, the execution will run on the Thread of the last completing Future in the sequence. +Then there's a method that's called ``fold`` that takes a start-value, +a sequence of ``Future``:s and a function from the type of the start-value, a timeout, +and the type of the futures and returns something with the same type as the start-value, +and then applies the function to all elements in the sequence of futures, non-blockingly, +the execution will be started when the last of the Futures is completed. .. includecode:: code/akka/docs/future/FutureDocTestBase.java :include: imports5,fold @@ -97,16 +149,52 @@ Then there's a method that's called ``fold`` that takes a start-value, a sequenc That's all it takes! -If the sequence passed to ``fold`` is empty, it will return the start-value, in the case above, that will be empty String. In some cases you don't have a start-value and you're able to use the value of the first completing Future in the sequence as the start-value, you can use ``reduce``, it works like this: +If the sequence passed to ``fold`` is empty, it will return the start-value, in the case above, that will be empty String. +In some cases you don't have a start-value and you're able to use the value of the first completing Future +in the sequence as the start-value, you can use ``reduce``, it works like this: .. includecode:: code/akka/docs/future/FutureDocTestBase.java :include: imports6,reduce -Same as with ``fold``, the execution will be done by the Thread that completes the last of the Futures, you can also parallize it by chunking your futures into sub-sequences and reduce them, and then reduce the reduced results again. +Same as with ``fold``, the execution will be started when the last of the Futures is completed, you can also parallelize +it by chunking your futures into sub-sequences and reduce them, and then reduce the reduced results again. This is just a sample of what can be done. +Callbacks +--------- + +Sometimes you just want to listen to a ``Future`` being completed, and react to that not by creating a new Future, but by side-effecting. +For this Akka supports ``onComplete``, ``onSuccess`` and ``onFailure``, of which the latter two are specializations of the first. + +.. includecode:: code/akka/docs/future/FutureDocTestBase.java + :include: onSuccess + +.. includecode:: code/akka/docs/future/FutureDocTestBase.java + :include: onFailure + +.. includecode:: code/akka/docs/future/FutureDocTestBase.java + :include: onComplete + +Auxiliary methods +----------------- + +``Future`` ``or`` combines 2 Futures into a new ``Future``, and will hold the successful value of the second ``Future` +if the first ``Future`` fails. + +.. includecode:: code/akka/docs/future/FutureDocTestBase.java + :include: or + +You can also combine two Futures into a new ``Future`` that will hold a tuple of the two Futures successful results, +using the ``zip`` operation. + +.. includecode:: code/akka/docs/future/FutureDocTestBase.java + :include: zip + Exceptions ---------- -Since the result of a ``Future`` is created concurrently to the rest of the program, exceptions must be handled differently. It doesn't matter if an ``UntypedActor`` or the dispatcher is completing the ``Future``, if an ``Exception`` is caught the ``Future`` will contain it instead of a valid result. If a ``Future`` does contain an ``Exception``, calling ``Await.result`` will cause it to be thrown again so it can be handled properly. \ No newline at end of file +Since the result of a ``Future`` is created concurrently to the rest of the program, exceptions must be handled differently. +It doesn't matter if an ``UntypedActor`` or the dispatcher is completing the ``Future``, if an ``Exception`` is caught +the ``Future`` will contain it instead of a valid result. If a ``Future`` does contain an ``Exception``, +calling ``Await.result`` will cause it to be thrown again so it can be handled properly. \ No newline at end of file