Promise and Future in combat

Source: Internet
Author: User
Tags web services

The previous chapter describes the Future type and how to use it to write high readability, high combination asynchronous execution code.

Future is just one part of the whole puzzle: it is a read-only type that allows you to use it to compute the value, or to handle errors that occur in calculations. But before that, there has to be a way to put that value in. In this chapter, you will see how to achieve this by Promise type. type Promise

Before, we passed a sequence of code blocks to the future method in Scala.concurrent, and given a executioncontext in the scope, it magically invokes the code block asynchronously, returning the result of a future type.

Although the way to get Future is simple, there are other ways to create Future instances and populate it, which is Promise. Promise allows you to put a value in the Future, but only once, Future once completed, can not be changed.

a Future instance is always associated with one (and only one) Promise instance. If you call the future method in REPL, you will find that the return is also a Promise:

Import concurrent. Future
import Concurrent. Future

scala> Import concurrent.future
import concurrent.future

scala> import concurrent. ExecutionContext.Implicits.global
import Concurrent. ExecutionContext.Implicits.global

scala> val f:future[string] = Future {"Hello world!"}
F:scala.concurrent.future[string] = scala.concurrent.impl.promise$defaultpromise@2b509249

You get the object is a defaultpromise, it implements the Future and Promise interface, but this is the specific implementation details (the interested readers can flip through its implementation of the source code), users only need to know the implementation of the Future and corresponding Promise The relationship between the two is very clear.

This small example illustrates that there is no other way to accomplish a Future except through Promise, and the Future method is only an auxiliary function that hides the specific implementation mechanism.

Now, let's move on and see how to use the Promise type directly. Give a Promise

When we talk about whether promises can be cashed, a familiar example is the campaign promises of those politicians.

Suppose the elected politician gave his voters a promise of tax cuts. This can be expressed in Promise[taxcut]:

Import concurrent. Promise Case
Class TaxCut (Reduction:int)
//either give the type as a type parameter to the factory method:
V Al TaxCut = Promise[taxcut] ()
//or give the compiler a hint by specifying the type of your Val:
val Taxcut2:prom Ise[taxcut] = Promise ()
//taxcut:scala.concurrent.promise[taxcut] = scala.concurrent.impl.promise$ Defaultpromise@66ae2a84
//taxcut2:scala.concurrent.promise[taxcut] = scala.concurrent.impl.promise$ Defaultpromise@346974c6

Once you create this Promise, you can invoke the future method on it to get the promised future:

Val Taxcutf:future[taxcut] = taxcut.future
 //' > Scala.concurrent.future[taxcut] '  Scala.concurrent.impl.promise$defaultpromise@66ae2a84

The returned Future may not be the same as Promise, but the same object is always returned with the Future method on the same Promise to ensure a one-to-one relationship between Promise and Future. End Commitment

Once you give a promise and tell the world that it will be cashed in the near future, it's best to try to make it happen. In Scala, you can end a Promise, either successfully or unsuccessfully. deliver on commitments

To successfully end a Promise, you can call its success method and deliver a result that everyone hopes to achieve:

  Taxcut.success (TaxCut (20))

After doing so, Promise can no longer write other values, and if you want to write again, you will have an exception.

At this point, the Future associated with the Promise is successfully completed, and the registered callback will start executing, or mapping the Future, then the mapping function should also be executed.

Generally, the completion of the Promise and the processing of the returned Future occur on a different thread. It is likely that you created the Promise and immediately returned the Future associated with it to the caller, while in fact another thread is still calculating it.

To illustrate this point, let's take a tax cut to give an example:

Object Government {
  def redeemcampaignpledge (): future[taxcut] = {
    val p = promise[taxcut] ()
    Future {
      println ("Starting the new legislative period.")
      Thread.Sleep
      p.success (taxcut)
      println ("We reduced the taxes! You must reelect US!!!! 1111 ")
    }
    p.future
  }
}

This example uses the Future companion object, but don't be confused by it, the point of this example is that Promise is not done in the caller's thread.

Now we're cashing in on the campaign manifesto, adding a OnComplete callback to the Future:

Import Scala.util. {Success, failure}
Val Taxcutf:future[taxcut] = Government.redeemcampaignpledge ()
println ("Now this they ' re elected, let ' s" if they r Emember their promises ... ")
taxcutf.oncomplete {case Success (TaxCut
  (reduction)) =>
    println (s) miracle! They really cut we taxes by $reduction percentage points! ")
  Case Failure (ex) =>
    println (S "They broke their promises! again! Because of a ${ex.getmessage} ")
}

Run this example multiple times to find out that the order of results in the display is indeterminate, and that the final callback function executes and goes into the successful case. Break a Promise

Politicians are accustomed to breaking promises, and Scala programmers can sometimes do just that. Call the failure method, pass an exception, and end Promise:

Case Class Lameexcuse (msg:string) extends Exception (msg)
Object Government {
  def redeemcampaignpledge (): Future[taxcut] = {
     val p = promise[taxcut] ()
     Future {
       println ("Starting the new legislative period.")
       Thread.Sleep (p.failure) (
       lameexcuse ("global Economy Crisis"))
       println ("We didn ' t fulfill our promises, But surely they ' ll understand. ')
     }
     P.future
   }
}

This Redeemcampaignpledge realization will eventually violate the promise. Once the Promise is finished with failure, it cannot be written again, just as the success method does. The associated Future will also end in failure.

If you already have a Try, you can end it by passing it directly to the Promise complete method. If this Try is a Success, the associated Future will complete successfully, otherwise it will fail. the programming practice based on Future

If you want to use the Future programming paradigm to increase the extensibility of your application, the application must be designed in a non-blocking mode from bottom to top. This means that basically all the functions in the application layer should be asynchronous and return to Future.

Now, a possible use scenario is to develop WEB applications. The popular Scala WEB framework allows you to return the response as Future[response, rather than wait until you complete the response before you return. This is important because it allows the WEB server to handle more connections with a small number of threads. By giving the server the ability to future[response, you can maximize the utilization of the server thread pool.

Furthermore, the application service may require multiple calls to the database tier and/or some external services, at which point a plurality of Future can be obtained, with a for statement to combine them into a new Future, which is simply readable. Eventually, the WEB layer turns such a Future into Future[response].

But how to achieve these in practice. There are three different scenarios to consider: non-blocking io

Applications are likely to involve a large number of IO operations. For example, you might want to interact with the database, or you might invoke other WEB services as a client.

If so, you can use a library that is based on Java non-blocking IO implementations, or you can use the Java NIO API directly or through libraries such as Netty. Such libraries can handle a large number of connections with a quantitative thread pool.

But if you want to develop such a library, it is more appropriate to deal directly with Promise. Blocking IO

Sometimes, there is no library based on NIO available. For example, most database drivers in the Java world use blocking IO. In WEB applications, if you use such a driver to initiate a large number of calls to access the database, remember that these calls occur on the server line Chengri. To avoid this problem, you can put all the code that needs to interact with the database into the future code block, like this:

Get back a future[resultset] or something similar:
Future {
  querydb (query)
}

Until now, we've all executed these blocks of code using an implicitly available global ExecutionContext. Generally, a better way is to create a dedicated executioncontext in the database layer. It can be executorservice from Java, which also means that you can adjust the thread pool asynchronously to perform database calls, and other parts of the application are unaffected.

Import java.util.concurrent.Executors
import concurrent. ExecutionContext
val executorservice = Executors.newfixedthreadpool (4)
val ExecutionContext = Executioncontext.fromexecutorservice (Executorservice)
long-running computations

Depending on the nature of the application, an application occasionally calls for long-running tasks that do not involve IO (CPU-intensive tasks) at all. These tasks should also not be executed in the server thread, so they need to be turned into Future:

Future {
  longrunningcomputation (data, moredata)
}

Also, it is best to have some proprietary executioncontext to handle these CPU-intensive computations. How these thread pool sizes are adjusted depends on the characteristics of the application, which is beyond the scope of this article.

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.