在上一篇文章中,我们看到了如何在onComplete
及其对等对象onSuccess
和onFailure
从Future
提取值。 我们还看到了如何在测试用例中使用Await.result
来阻止并从Future中获取值。 在本文中,我们将简要讨论Promise
和Future
之间的关系。
诺言
Promise
和Future
的概念并存。 scala.concurrent.Promise
是为Future
设置值的一个。 换句话说, Promise
是异步执行计算的背后的大脑,而Future
只是在结果可用时读取结果的句柄。 简而言之, Promise
是推动者, Future
是推动者。
大多数情况下,我们不需要显式创建Promise。 但是,我们必须了解什么是Promise
,才能真正了解Future
工作方式。
让我们使用以下示例来了解如何创建Promise
。
1.完成承诺
在下面的代码中,我们将看到如何在Promise中设置值以及如何在另一侧读取它。
我们
1. Promise
2.通过设置成功值来complete
Promise
3.然后通过使用promise.future
将Promise – Future
的已读面返回给调用方
幕后没有耗时的过程。 该值立即设置为Promise,因此可以通过Future
立即获得该值。
码
class PromiseInternals {
...
def aCompletedPromiseUsingSuccess(num:Int): Future[Int] = {
val promise=Promise[Int]()
promise.success(num)
promise.future
}
...
...
测试用例
当我们运行测试用例代码时,将在调用promise.success(100)
之后立即调用onComplete
回调。
class PromiseInternalsTest extends FunSpec with Matchers {
describe("A Future") {
it("gives out the correct value when a Promise is completed") {
val promiseInternals = new PromiseInternals
val aCompletedPromise=promiseInternals.aCompletedPromiseUsingSuccess(100)
assertValue(aCompletedPromise, 100)
}
...
...
def assertValueUsingOnComplete(future: Future[Int], expectedValue: Int): Unit = {
future.onComplete {
case Success(result) => {
println (s"Result is $result and expectedValue is $expectedValue")
result shouldBe expectedValue
}
case Failure (msg) => fail(msg)
}
}
promise.success
只是使用promise.complete
的快捷方式,它接受Try[T]
作为参数。 因此,我们实际上可以将上述函数编写为:
def aCompletedPromiseUsingComplete(num:Int): Future[Int] = {
val promise=Promise[Int]()
promise.complete(Success(num))
promise.future
}
或者,如果我们想指示计算失败,则可以使用promise.complete(Failure(throwable))
或
def aCompletedPromiseUsingFailure(num:Int): Future[Int] = {
val promise=Promise[Int]()
promise.failure(new RuntimeException("Evil Exception"))
promise.future
}
让我们在图片中总结以上内容:
2.异步运行块
现在,我们了解了如何通过设置成功值或异常来完成Promise,我们将看到如何异步执行代码块。
在以下测试用例中,我们将代码块传递给someExternalDelayedCalculation
以异步执行。
测试用例
让我们先看一下测试用例。
- 我们将一个块作为参数传递。 代码块仅睡眠2秒钟,然后返回100。
- 3秒后确认该值。
很简单。
it("gives out the correct value when an asynchronous block is submitted and is completed through a Promise") {
val promiseInternals = new PromiseInternals
val longCalculationFuture = promiseInternals.someExternalDelayedCalculation{()=>
Thread.sleep(2000)
100
}
println (s"We have submitted a block to be executed asynchronously ${longCalculationFuture.isCompleted}") //false at this point
assertValue(longCalculationFuture, 100)
}
def assertValue(future: Future[Int], expectedValue: Int): Unit = {
val resultVal=Await.result(future, 3000 seconds)
resultVal shouldBe expectedValue
}
码
someExternalDelayedCalculation
的实现很有趣:
我们
1.创建一个FixedThreadPool
以执行我们的异步代码。
2.创建一个承诺 3.创建一个Runnable
并在run
方法中包装要异步运行的块 4.关闭承诺并使用run
结果完成承诺 5.在somePool
执行Runnable
。 6.返回promise.future
,调用方可以从中读取值。
val somePool=Executors.newFixedThreadPool(2)
def someExternalDelayedCalculation(f:()=>Int): Future[Int] = {
val promise=Promise[Int]()
val thisIsWhereWeCallSomeExternalComputation = new Runnable {
override def run(): Unit ={
promise.complete{
try(Success(f()))
catch {
case NonFatal (msg)=> Failure(msg)
}
}
}
}
somePool.execute(thisIsWhereWeCallSomeExternalComputation)
promise.future
}
而已 !!
3.
好吧,我被骗了。 项目符号2中的代码实际上是从Future.apply
本身的实际实现中Future.apply
。
记得在上一篇文章中,我们看到当代码块传递到Future
的apply函数中时,它是异步执行的。
现在,将someExternalDelayedCalculation
中的上述代码与someExternalDelayedCalculation
的实际实现及其Future.apply
的Runnable
进行比较。
def apply[T](body: =>T)(implicit executor: ExecutionContext): scala.concurrent.Future[T] = {
val runnable = new PromiseCompletingRunnable(body)
executor.prepare.execute(runnable)
runnable.promise.future
}
class PromiseCompletingRunnable[T](body: => T) extends Runnable {
val promise = new Promise.DefaultPromise[T]()
override def run() = {
promise complete {
try Success(body) catch { case NonFatal(e) => Failure(e) }
}
}
}
要重复上述步骤,请执行apply
函数
- 拥有我们作为隐式
ExecutionContext
提供的ThreadPool
- 创建一个
Promise
通过创建PromiseCompletingRunnable
这是一个Runnable
- 在
run
方法中包装要异步运行的块 - 使用
run
结果完成承诺并完成承诺 - 使用
ExecutionContext
执行Runnable
- 返回
promise.future
,调用者可以从中读取值。
4.一旦写入,两次出错
一旦诺言Success
或Failure
都完成了,那么我们要做的就是从Future
提取价值。 此外,还会调用Future的onComplete
回调。 包装在“承诺的未来”中的价值是固定不变的,无法更改。
如果我们尝试通过完成已经完成的Promise来设置新值,则抛出IllegalStateException
。
码
让我们用一个片段来看看。 在下面的代码中,我们创建一个Promise并用100来完成它。然后,我们尝试以失败来完成它。
def alreadyCompletedPromise(): Future[Int] = {
val promise = Promise[Int]()
promise.success(100) //completed
promise.failure(new RuntimeException("Will never be set because an IllegalStateException will be thrown beforehand"))
promise.future
}
测试用例
测试用例仅断言,尝试以失败完成Promise时会抛出IllegalStateException
。
it("should throw an error if a Promise is attempted to be completed more than once") {
val promiseInternals = new PromiseInternals
intercept[IllegalStateException] {
promiseInternals.alreadyCompletedPromise()
}
}
码
支持此博客的完整代码可在github中找到
翻译自: https://www.javacodegeeks.com/2016/06/scala-notes-futures-2-promises.html