Future and Promise
The future is a sort of placeholder object, the create for a result, does not exist. Generally, the result of the computed concurrently and can be later collected. Composing concurrent tasks in this is tends to faster, asynchronous, non-blocking parallel code.
By default, futures and promises is non-blocking, make use of callbacks instead of typical blocking operations. To simplify the use of callbacks both syntactically (syntactically) and conceptually, Scala provides combinators (combo) such as FlatMap , foreach and filter used to compose the future in a non-blocking.
Future
A future was an object holding a value which would be available at some point. This value was usually the result of some other computation. The state of the future could is complete (with a value or exception) and uncomplete. Complete can take one of the forms:success (return a value) or failed (return a exception).
The future cannot was rewrite, it is immutable.
import scala.concurrent._ // make future availableimport ExcecutionContext.Implicits.globalval session = socialNetwork.createSessionFor("user", credentials)val f: Future[List[Friend]] = future { session.getFriends() }
The IF session is null and then the future would throw a nullpointexception. About ExecutionContext:
Execution context execute tasks submitted to them, and you can think executions contexts as thread pools. They was essential for the future method because they handle how and when the asynchronous computation was executed. Another example
val firstOccurrence: Future[Int] = future { val source = scala.io.Source.fromFile("myText.txt") source.toSeq.indexOfSlice("myKeyword")}
Callbacks
A better-to-do it's a complete non-blocking-the-registering a callback on the future. The callback is called asynchronously once, the future is available. The callback function is executed asynchronously or sequentially on the same thread.
The most general form of registering a callback are by using the OnComplete method which takes a callback function of type Try[t] = U. The callback is applied to the value of type success[t] If the future completes successfully, or to a value of type Failur E[t] otherwise.
Try[t] is similar to option[t] or either[t, S], in that it's a monad that potentially holding some type. Option can be a some[t] or None. TRY[T] was success[t] when it holds a value and otherwise failure[t]. FAILURE[T] holds more information, just a plain NONE by saying, the value is not there.
import scala.util.{Success, Failure}val f: Future[List[String]] = future { session.getRecentPosts}f onComplete { case Success(posts) => for (post <- posts) println(post) case Failure(t) => println("Error")}
val f: Future[List[String]] = future { session.getRecentPosts }f onSuccess { case posts => for(post <- posts) println(post)}f onFailure { case t => println("Error: " + t.getMessage)}
? Since partial functions has the isdefinedat method, the OnFailure trigger callback if it is defined for a partic Ular throwable.
val f = future { 2 / 0}f onFailure { // this failure never be triggered case npe: NullPointerException => println("I‘d be amazed if this printed out.")}
*return Unit The OnComplete, onsuccess and OnFailure methods have the result type Unit, which means the invocation of these methods can Not being chained. Note that this design is intentional because callbacks registered on the same future is unordered.
Functional Composition and For-comprehension
Inline combination
val rateQuate = future { connection.getCurrentValue(USD)}rateQuote onSuccess { case quote => val purchase = future { if(isProfitable(quote)) connection.buy(amout, quote) else throw new Exception("not profitable") } purchase onSuccess { case _ => println("Purchase " + amount + " USD") }}
This works, but it was inconvenient for the reasons. First, we had to use onsuccess, and we had to nest the second purchase a future within it. Second, purchase is within Ratequote onsuccess so the rest part of this code cannot see this variable. For this and reasons, futures combinators which allow a more straightforward composition. Using Map:
val rateQute = future {...}val purchase = rateQuote map { quote => if(isProfitable(quote)) connection.buy(amout, quote) else throw new Exception("not profitable")}purchase onSuccess { case _ => println("Purchased " + amount + " USD")}
By using a map on ratequote we had eliminated one onsuccess callback and, more importantly, the nesting. But the map still cannot deal with exception.
So, we need flatMap, filter and foreach combinators.
val usdQuote = future {connection.getCurrentValue(USD)}val chfQuote = future {connection.getCurrentValue{CHF}}val purchase = for { usd <- usdQuote chf <- chfQuote if isProfitable(usd, chf)} yield connection.buy(amout, chf)
The purchase completed only once both Usdquote and Chfquote are completed.
Another example is the use of multiple sub-groups, although not commonly used, but it is helpful to understand the composition
val purchase = usdQuote flatMap { usd => chfQuote .withFilter(chf => isProfitable(usd, chf)) .map(chf => connection.buy(amount, chf))}
FlatMap operation maps its own value to some other future. Once this different-completed, the resulting future is completed with its value. Use to Flatmap place is not understand
The filter combinator creates a new future which contains the value of the original future only if it satisfies some predi Cate.
val purchase : Future[Int] = rateQuote map { quote => connection.buy(amount, quote)} recover { case QuoteChangeException() => 0}
Quotechangeexception is self-made, representing a class of exceptions.
The Recover Combinator create new future which hold the same result as the original of the future if it is complete successfully. If it did not do not the partial function would be is applied to throwable which failed the future. It maps the throwable to some value and the new future is completed. If the partial function is not defined on that throwable, then original exception would be returned.
Recoverywith to recover are similar to, the FlatMap to map.
Combinator Fallbackto Create a new future holds the original result if it is complete successfully, or otherwise the SUCCESSF UL result of the argument future. If both failed, the new future was complete with this exception (?).
val usedQuote = future { connection.getCurrentValue(USD)} map { usd => "Value: " + usd + "$"}val chfQuote = future { connection.getCurrentValue(CHF)} map { chf => "Value: " + chf + "CHF"}val anyQuote = usdQuote fallbackto chfQuoteanyQuote onSuccess {println(_)}
Andthen, it return new future with the exactly same result as the current future, regardless of whether the Curre NT success or not.
val allPosts = mutable.Set[String]()future { session.getRecentPosts} andThen { posts => allPosts ++= posts} andThen { posts => clearAll() for(post <- posts) render(post)}
In summary, the combinator on futures is purely functional. It returns a future which are related to the future it derived form.
Projections
val f = future { 2 / 0}for(exc <- f.failed) print(exc) // 会打印出exception信息
Promise
The future can also is created from promise.
A promise can is thought of as a writable, Single-assignment container, which completes a future.
That's, a promise can be used to successfully complete a future with a value using the Success method. (when value is ready, the future is ready)
Conversely, a promise can also to is used to complete a future with a exception, by failing the promise, using the failure Method. (This time R returns to exception bar)
A Promise P completes the future returned by P.future. This is specific to the Promise p. depending on the implementation, it could be the case that P.future eq p. (Promise is a container that can be used to deliver messages)
import scala.concurrent.{future, promise}import scala.concurrent.ExecutionContext.Implicits.globalval p = promise[T]val f = p.futureval producer = future { val r = produceSomething() p success r //当r就绪时p.future就绪 continueDoingSomethingUnrelated()}val consumer = future { startDoingSomething() f onSuccess { case r => doSomethingWithResult() }}//producer will produce failureval producer = future { if(isValid(r)) p failure (new IllegalStateException) else { val q = doSomethingComputation(r) p success q }}
Calling success on a promise it has already been completed (or failed) would throw an illegalstateexception
One nice property of programs written using promises with operations described so far and futures which is composed throu GH monadic operations without side-effects is the these programs is deterministic. Deterministic here means this, given that no exception are thrown in the program, the result of the program (values observed In the futures) would always be the same, regardless of the execution schedule of the parallel program.
The method Completewith completes the promise with a another future. After the completed, the promise gets completed with the result of the.
val f = future {1}val p = promise[Int]p completeWith fp.future onSuccess { case x => println(x)}
Using Promise, the OnComplete method of the futures and the future construct you can implement any of the functional compo Sition Combinators described earlier.
def first[T](f: Future[T], g: Future[T]): Future[T] = { val p = promise[T] //不知道下面的代码能不能换成completeWith f onSuccess { case x => p.trySuccess(x) } g onSuccess { case x => p.trySuccess(x) } p.future}
Utilities
Duration is widely used in the Akka library, which is a special data type that can be represented as a wireless (Duration.inf) Duration.minusinf or a limited time period
With Scala, time periods can be created with a small DSL and support all the desired types
import akka.util.duration._val fivesec = 5.secondsval threemillis = 3.millisval diff = fivesec - threemillisassert(diff < fivesec)val fourmills = threemillis * 4 / 3val n = threemillis / (1 millisecond)
Abstract Duration contains methods:
Conversion to different time units (Tonano, Tomicros, Tomills, Tosecond, Tominutes)
Comparison of durations (<, <=, >)
Arithmetic operations (+,-, *)
Minimum and Maximum (max, min)
Check if the duration is finite
Duration
Implicitly from Types Int and Long. Val d = Millis
By passing a Long length and a timeunit type. Val d = Duration (+, MILLISECONDS)
By parsing a string that represent a time period. Val d = Duration ("1.2 Us")
val d1 = Duration(100, MIILISECONDS)val d2 = Duration(100, "millis")val d3 = 100 millisval d4 = Duration("1.2us")// pattern matchingval Duration(length, unit) = 5 millis
Deadline
Duration has a sister class called Deadline, which represents an absolute point in time and supports calculating the gap between the current time and the deadline to generate Duration
val deadline = 10 seconds fromNowawaitCond(..., deadline.timeLeft)
These classes have a corresponding implementation in Java.
Future and promise