#2643 - Removing the use of Await in the Future documentation, promoting the use of callbacks
This commit is contained in:
parent
75dc721b16
commit
be0d5b4509
4 changed files with 121 additions and 31 deletions
|
|
@ -79,6 +79,21 @@ public class FutureDocTestBase {
|
|||
system.shutdown();
|
||||
}
|
||||
|
||||
public final static class PrintResult<T> extends OnSuccess<T> {
|
||||
@Override public final void onSuccess(T t) {
|
||||
// print t
|
||||
}
|
||||
}
|
||||
|
||||
public final static class Demo {
|
||||
//#print-result
|
||||
public final static class PrintResult<T> extends OnSuccess<T> {
|
||||
@Override public final void onSuccess(T t) {
|
||||
System.out.println(t);
|
||||
}
|
||||
}
|
||||
//#print-result
|
||||
}
|
||||
@SuppressWarnings("unchecked") @Test public void useCustomExecutionContext() throws Exception {
|
||||
ExecutorService yourExecutorServiceGoesHere = Executors.newSingleThreadExecutor();
|
||||
//#diy-execution-context
|
||||
|
|
@ -102,6 +117,9 @@ public class FutureDocTestBase {
|
|||
Future<Object> future = Patterns.ask(actor, msg, timeout);
|
||||
String result = (String) Await.result(future, timeout.duration());
|
||||
//#ask-blocking
|
||||
//#pipe-to
|
||||
akka.pattern.Patterns.pipe(future, system.dispatcher()).to(actor);
|
||||
//#pipe-to
|
||||
assertEquals("HELLO", result);
|
||||
}
|
||||
|
||||
|
|
@ -113,9 +131,11 @@ public class FutureDocTestBase {
|
|||
return "Hello" + "World";
|
||||
}
|
||||
}, system.dispatcher());
|
||||
String result = (String) Await.result(f, Duration.create(5, SECONDS));
|
||||
|
||||
f.onSuccess(new PrintResult<String>(), system.dispatcher());
|
||||
//#future-eval
|
||||
assertEquals("HelloWorld", result);
|
||||
String result = (String) Await.result(f, Duration.create(5, SECONDS));
|
||||
assertEquals("HelloWorld", result);
|
||||
}
|
||||
|
||||
@Test
|
||||
|
|
@ -135,9 +155,10 @@ public class FutureDocTestBase {
|
|||
}
|
||||
}, ec);
|
||||
|
||||
f2.onSuccess(new PrintResult<Integer>(), system.dispatcher());
|
||||
//#map
|
||||
int result = Await.result(f2, Duration.create(5, SECONDS));
|
||||
assertEquals(10, result);
|
||||
//#map
|
||||
}
|
||||
|
||||
@Test
|
||||
|
|
@ -158,6 +179,7 @@ public class FutureDocTestBase {
|
|||
}
|
||||
}, ec);
|
||||
|
||||
f2.onSuccess(new PrintResult<Integer>(), system.dispatcher());
|
||||
//#map2
|
||||
int result = Await.result(f2, Duration.create(5, SECONDS));
|
||||
assertEquals(10, result);
|
||||
|
|
@ -174,7 +196,8 @@ public class FutureDocTestBase {
|
|||
}
|
||||
}, ec);
|
||||
|
||||
Thread.sleep(100);
|
||||
// Thread.sleep is only here to prove a point
|
||||
Thread.sleep(100); // Do not use this in your code
|
||||
|
||||
Future<Integer> f2 = f1.map(new Mapper<String, Integer>() {
|
||||
public Integer apply(String s) {
|
||||
|
|
@ -182,6 +205,7 @@ public class FutureDocTestBase {
|
|||
}
|
||||
}, ec);
|
||||
|
||||
f2.onSuccess(new PrintResult<Integer>(), system.dispatcher());
|
||||
//#map3
|
||||
int result = Await.result(f2, Duration.create(5, SECONDS));
|
||||
assertEquals(10, result);
|
||||
|
|
@ -208,6 +232,7 @@ public class FutureDocTestBase {
|
|||
}
|
||||
}, ec);
|
||||
|
||||
f2.onSuccess(new PrintResult<Integer>(), system.dispatcher());
|
||||
//#flat-map
|
||||
int result = Await.result(f2, Duration.create(5, SECONDS));
|
||||
assertEquals(10, result);
|
||||
|
|
@ -238,8 +263,9 @@ public class FutureDocTestBase {
|
|||
}
|
||||
}, ec);
|
||||
|
||||
long result = Await.result(futureSum, Duration.create(5, SECONDS));
|
||||
futureSum.onSuccess(new PrintResult<Long>(), system.dispatcher());
|
||||
//#sequence
|
||||
long result = Await.result(futureSum, Duration.create(5, SECONDS));
|
||||
assertEquals(3L, result);
|
||||
}
|
||||
|
||||
|
|
@ -262,9 +288,10 @@ public class FutureDocTestBase {
|
|||
}, ec);
|
||||
|
||||
//Returns the sequence of strings as upper case
|
||||
futureResult.onSuccess(new PrintResult<Iterable<String>>(), system.dispatcher());
|
||||
//#traverse
|
||||
Iterable<String> result = Await.result(futureResult, Duration.create(5, SECONDS));
|
||||
assertEquals(Arrays.asList("A", "B", "C"), result);
|
||||
//#traverse
|
||||
}
|
||||
|
||||
@Test
|
||||
|
|
@ -286,9 +313,10 @@ public class FutureDocTestBase {
|
|||
return r + t; //Just concatenate
|
||||
}
|
||||
}, ec);
|
||||
String result = Await.result(resultFuture, Duration.create(5, SECONDS));
|
||||
//#fold
|
||||
|
||||
resultFuture.onSuccess(new PrintResult<String>(), system.dispatcher());
|
||||
//#fold
|
||||
String result = Await.result(resultFuture, Duration.create(5, SECONDS));
|
||||
assertEquals("ab", result);
|
||||
}
|
||||
|
||||
|
|
@ -310,8 +338,9 @@ public class FutureDocTestBase {
|
|||
}
|
||||
}, ec);
|
||||
|
||||
Object result = Await.result(resultFuture, Duration.create(5, SECONDS));
|
||||
resultFuture.onSuccess(new PrintResult<Object>(), system.dispatcher());
|
||||
//#reduce
|
||||
Object result = Await.result(resultFuture, Duration.create(5, SECONDS));
|
||||
|
||||
assertEquals("ab", result);
|
||||
}
|
||||
|
|
@ -399,9 +428,11 @@ public class FutureDocTestBase {
|
|||
throw problem;
|
||||
}
|
||||
}, ec);
|
||||
|
||||
future.onSuccess(new PrintResult<Integer>(), system.dispatcher());
|
||||
//#recover
|
||||
int result = Await.result(future, Duration.create(5, SECONDS));
|
||||
assertEquals(result, 0);
|
||||
//#recover
|
||||
}
|
||||
|
||||
@Test
|
||||
|
|
@ -425,9 +456,11 @@ public class FutureDocTestBase {
|
|||
throw problem;
|
||||
}
|
||||
}, ec);
|
||||
|
||||
future.onSuccess(new PrintResult<Integer>(), system.dispatcher());
|
||||
//#try-recover
|
||||
int result = Await.result(future, Duration.create(5, SECONDS));
|
||||
assertEquals(result, 0);
|
||||
//#try-recover
|
||||
}
|
||||
|
||||
@Test
|
||||
|
|
@ -497,9 +530,10 @@ public class FutureDocTestBase {
|
|||
}
|
||||
}, ec);
|
||||
|
||||
future3.onSuccess(new PrintResult<String>(), system.dispatcher());
|
||||
//#zip
|
||||
String result = Await.result(future3, Duration.create(5, SECONDS));
|
||||
assertEquals("foo bar", result);
|
||||
//#zip
|
||||
}
|
||||
|
||||
{
|
||||
|
|
@ -509,9 +543,10 @@ public class FutureDocTestBase {
|
|||
Future<String> future3 = Futures.successful("bar");
|
||||
// Will have "bar" in this case
|
||||
Future<String> future4 = future1.fallbackTo(future2).fallbackTo(future3);
|
||||
future4.onSuccess(new PrintResult<String>(), system.dispatcher());
|
||||
//#fallback-to
|
||||
String result = Await.result(future4, Duration.create(5, SECONDS));
|
||||
assertEquals("bar", result);
|
||||
//#fallback-to
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -47,6 +47,17 @@ Alternatives to blocking are discussed further within this documentation.
|
|||
Also note that the ``Future`` returned by an ``UntypedActor`` is a ``Future<Object>`` since an ``UntypedActor`` is dynamic.
|
||||
That is why the cast to ``String`` is used in the above sample.
|
||||
|
||||
.. warning::
|
||||
|
||||
``Await.result`` and ``Await.ready`` are provided for exceptional situations where you **must** block,
|
||||
a good rule of thumb is to only use them if you know why you **must** block. For all other cases, use
|
||||
asynchronous composition as described below.
|
||||
|
||||
To send the result of a ``Future`` to an ``Actor``, you can use the ``pipe`` construct:
|
||||
|
||||
.. includecode:: code/docs/future/FutureDocTestBase.java
|
||||
:include: pipe-to
|
||||
|
||||
Use Directly
|
||||
------------
|
||||
|
||||
|
|
@ -75,6 +86,11 @@ Or failures:
|
|||
.. includecode:: code/docs/future/FutureDocTestBase.java
|
||||
:include: failed
|
||||
|
||||
For these examples ``PrintResult`` is defined as follows:
|
||||
|
||||
.. includecode:: code/docs/future/FutureDocTestBase.java
|
||||
:include: print-result
|
||||
|
||||
Functional Futures
|
||||
------------------
|
||||
|
||||
|
|
|
|||
|
|
@ -39,6 +39,9 @@ object FutureDocSpec {
|
|||
class FutureDocSpec extends AkkaSpec {
|
||||
import FutureDocSpec._
|
||||
import system.dispatcher
|
||||
|
||||
val println: PartialFunction[Any, Unit] = { case _ ⇒ }
|
||||
|
||||
"demonstrate usage custom ExecutionContext" in {
|
||||
val yourExecutorServiceGoesHere = java.util.concurrent.Executors.newSingleThreadExecutor()
|
||||
//#diy-execution-context
|
||||
|
|
@ -68,6 +71,12 @@ class FutureDocSpec extends AkkaSpec {
|
|||
val future = actor ? msg // enabled by the “ask” import
|
||||
val result = Await.result(future, timeout.duration).asInstanceOf[String]
|
||||
//#ask-blocking
|
||||
|
||||
//#pipe-to
|
||||
import akka.pattern.pipe
|
||||
future pipeTo actor
|
||||
//#pipe-to
|
||||
|
||||
result must be("HELLO")
|
||||
}
|
||||
|
||||
|
|
@ -93,9 +102,9 @@ class FutureDocSpec extends AkkaSpec {
|
|||
val future = Future {
|
||||
"Hello" + "World"
|
||||
}
|
||||
val result = Await.result(future, 1 second)
|
||||
future foreach println
|
||||
//#future-eval
|
||||
result must be("HelloWorld")
|
||||
Await.result(future, 1 second) must be("HelloWorld")
|
||||
}
|
||||
|
||||
"demonstrate usage of map" in {
|
||||
|
|
@ -106,10 +115,11 @@ class FutureDocSpec extends AkkaSpec {
|
|||
val f2 = f1 map { x ⇒
|
||||
x.length
|
||||
}
|
||||
f2 foreach println
|
||||
//#map
|
||||
val result = Await.result(f2, 1 second)
|
||||
result must be(10)
|
||||
f1.value must be(Some(Success("HelloWorld")))
|
||||
//#map
|
||||
}
|
||||
|
||||
"demonstrate wrong usage of nested map" in {
|
||||
|
|
@ -123,6 +133,7 @@ class FutureDocSpec extends AkkaSpec {
|
|||
x.length * y
|
||||
}
|
||||
}
|
||||
f3 foreach println
|
||||
//#wrong-nested-map
|
||||
Await.ready(f3, 1 second)
|
||||
}
|
||||
|
|
@ -138,25 +149,30 @@ class FutureDocSpec extends AkkaSpec {
|
|||
x.length * y
|
||||
}
|
||||
}
|
||||
f3 foreach println
|
||||
//#flat-map
|
||||
val result = Await.result(f3, 1 second)
|
||||
result must be(30)
|
||||
//#flat-map
|
||||
}
|
||||
|
||||
"demonstrate usage of filter" in {
|
||||
//#filter
|
||||
val future1 = Future.successful(4)
|
||||
val future2 = future1.filter(_ % 2 == 0)
|
||||
val result = Await.result(future2, 1 second)
|
||||
result must be(4)
|
||||
|
||||
future2 foreach println
|
||||
|
||||
val failedFilter = future1.filter(_ % 2 == 1).recover {
|
||||
// When filter fails, it will have a java.util.NoSuchElementException
|
||||
case m: NoSuchElementException ⇒ 0
|
||||
}
|
||||
|
||||
failedFilter foreach println
|
||||
//#filter
|
||||
val result = Await.result(future2, 1 second)
|
||||
result must be(4)
|
||||
val result2 = Await.result(failedFilter, 1 second)
|
||||
result2 must be(0) //Can only be 0 when there was a MatchError
|
||||
//#filter
|
||||
}
|
||||
|
||||
"demonstrate usage of for comprehension" in {
|
||||
|
|
@ -171,9 +187,10 @@ class FutureDocSpec extends AkkaSpec {
|
|||
// Note that the execution of futures a, b, and c
|
||||
// are not done in parallel.
|
||||
|
||||
f foreach println
|
||||
//#for-comprehension
|
||||
val result = Await.result(f, 1 second)
|
||||
result must be(24)
|
||||
//#for-comprehension
|
||||
}
|
||||
|
||||
"demonstrate wrong way of composing" in {
|
||||
|
|
@ -220,8 +237,9 @@ class FutureDocSpec extends AkkaSpec {
|
|||
c ← ask(actor3, (a + b)).mapTo[Int]
|
||||
} yield c
|
||||
|
||||
val result = Await.result(f3, 1 second).asInstanceOf[Int]
|
||||
f3 foreach println
|
||||
//#composing
|
||||
val result = Await.result(f3, 1 second).asInstanceOf[Int]
|
||||
result must be(3)
|
||||
}
|
||||
|
||||
|
|
@ -236,25 +254,28 @@ class FutureDocSpec extends AkkaSpec {
|
|||
val futureList = Future.sequence(listOfFutures)
|
||||
|
||||
// Find the sum of the odd numbers
|
||||
val oddSum = Await.result(futureList.map(_.sum), 1 second).asInstanceOf[Int]
|
||||
oddSum must be(10000)
|
||||
val oddSum = futureList.map(_.sum)
|
||||
oddSum foreach println
|
||||
//#sequence-ask
|
||||
Await.result(oddSum, 1 second).asInstanceOf[Int] must be(10000)
|
||||
}
|
||||
|
||||
"demonstrate usage of sequence" in {
|
||||
//#sequence
|
||||
val futureList = Future.sequence((1 to 100).toList.map(x ⇒ Future(x * 2 - 1)))
|
||||
val oddSum = Await.result(futureList.map(_.sum), 1 second).asInstanceOf[Int]
|
||||
oddSum must be(10000)
|
||||
val oddSum = futureList.map(_.sum)
|
||||
oddSum foreach println
|
||||
//#sequence
|
||||
Await.result(oddSum, 1 second).asInstanceOf[Int] must be(10000)
|
||||
}
|
||||
|
||||
"demonstrate usage of traverse" in {
|
||||
//#traverse
|
||||
val futureList = Future.traverse((1 to 100).toList)(x ⇒ Future(x * 2 - 1))
|
||||
val oddSum = Await.result(futureList.map(_.sum), 1 second).asInstanceOf[Int]
|
||||
oddSum must be(10000)
|
||||
val oddSum = futureList.map(_.sum)
|
||||
oddSum foreach println
|
||||
//#traverse
|
||||
Await.result(oddSum, 1 second).asInstanceOf[Int] must be(10000)
|
||||
}
|
||||
|
||||
"demonstrate usage of fold" in {
|
||||
|
|
@ -262,8 +283,9 @@ class FutureDocSpec extends AkkaSpec {
|
|||
// Create a sequence of Futures
|
||||
val futures = for (i ← 1 to 1000) yield Future(i * 2)
|
||||
val futureSum = Future.fold(futures)(0)(_ + _)
|
||||
Await.result(futureSum, 1 second) must be(1001000)
|
||||
futureSum foreach println
|
||||
//#fold
|
||||
Await.result(futureSum, 1 second) must be(1001000)
|
||||
}
|
||||
|
||||
"demonstrate usage of reduce" in {
|
||||
|
|
@ -271,8 +293,9 @@ class FutureDocSpec extends AkkaSpec {
|
|||
// Create a sequence of Futures
|
||||
val futures = for (i ← 1 to 1000) yield Future(i * 2)
|
||||
val futureSum = Future.reduce(futures)(_ + _)
|
||||
Await.result(futureSum, 1 second) must be(1001000)
|
||||
futureSum foreach println
|
||||
//#reduce
|
||||
Await.result(futureSum, 1 second) must be(1001000)
|
||||
}
|
||||
|
||||
"demonstrate usage of recover" in {
|
||||
|
|
@ -283,6 +306,7 @@ class FutureDocSpec extends AkkaSpec {
|
|||
val future = akka.pattern.ask(actor, msg1) recover {
|
||||
case e: ArithmeticException ⇒ 0
|
||||
}
|
||||
future foreach println
|
||||
//#recover
|
||||
Await.result(future, 1 second) must be(0)
|
||||
}
|
||||
|
|
@ -297,6 +321,7 @@ class FutureDocSpec extends AkkaSpec {
|
|||
case foo: IllegalArgumentException ⇒
|
||||
Future.failed[Int](new IllegalStateException("All br0ken!"))
|
||||
}
|
||||
future foreach println
|
||||
//#try-recover
|
||||
Await.result(future, 1 second) must be(0)
|
||||
}
|
||||
|
|
@ -306,6 +331,7 @@ class FutureDocSpec extends AkkaSpec {
|
|||
val future2 = Future { "bar" }
|
||||
//#zip
|
||||
val future3 = future1 zip future2 map { case (a, b) ⇒ a + " " + b }
|
||||
future3 foreach println
|
||||
//#zip
|
||||
Await.result(future3, 1 second) must be("foo bar")
|
||||
}
|
||||
|
|
@ -321,6 +347,7 @@ class FutureDocSpec extends AkkaSpec {
|
|||
} andThen {
|
||||
case _ ⇒ watchSomeTV
|
||||
}
|
||||
result foreach println
|
||||
//#and-then
|
||||
Await.result(result, 1 second) must be("foo bar")
|
||||
}
|
||||
|
|
@ -331,6 +358,7 @@ class FutureDocSpec extends AkkaSpec {
|
|||
val future3 = Future { "pigdog" }
|
||||
//#fallback-to
|
||||
val future4 = future1 fallbackTo future2 fallbackTo future3
|
||||
future4 foreach println
|
||||
//#fallback-to
|
||||
Await.result(future4, 1 second) must be("foo")
|
||||
}
|
||||
|
|
|
|||
|
|
@ -41,7 +41,7 @@ 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``.
|
||||
|
||||
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:
|
||||
Using an ``Actor``\'s ``?`` method to send a message will return a ``Future``:
|
||||
|
||||
.. includecode:: code/docs/future/FutureDocSpec.scala
|
||||
:include: ask-blocking
|
||||
|
|
@ -59,6 +59,11 @@ When using non-blocking it is better to use the ``mapTo`` method to safely try t
|
|||
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.
|
||||
|
||||
To send the result of a ``Future`` to an ``Actor``, you can use the ``pipe`` construct:
|
||||
|
||||
.. includecode:: code/docs/future/FutureDocSpec.scala
|
||||
:include: pipe-to
|
||||
|
||||
Use Directly
|
||||
------------
|
||||
|
||||
|
|
@ -150,6 +155,12 @@ First an example of using ``Await.result``:
|
|||
.. includecode:: code/docs/future/FutureDocSpec.scala
|
||||
:include: composing-wrong
|
||||
|
||||
.. warning::
|
||||
|
||||
``Await.result`` and ``Await.ready`` are provided for exceptional situations where you **must** block,
|
||||
a good rule of thumb is to only use them if you know why you **must** block. For all other cases, use
|
||||
asynchronous composition as described below.
|
||||
|
||||
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:
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue