Scala笔记–期货– 2(承诺)

在上一篇文章中,我们看到了如何在onComplete及其对等对象onSuccessonFailureFuture提取值。 我们还看到了如何在测试用例中使用Await.result来阻止并从Future中获取值。 在本文中,我们将简要讨论PromiseFuture之间的关系。

诺言

PromiseFuture的概念并存。 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以异步执行。

测试用例

让我们先看一下测试用例。

  1. 我们将一个块作为参数传递。 代码块仅睡眠2秒钟,然后返回100。
  2. 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.applyRunnable进行比较。

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函数

  1. 拥有我们作为隐式ExecutionContext提供的ThreadPool
  2. 创建一个Promise通过创建PromiseCompletingRunnable这是一个Runnable
  3. run方法中包装要异步运行的块
  4. 使用run结果完成承诺并完成承诺
  5. 使用ExecutionContext执行Runnable
  6. 返回promise.future ,调用者可以从中读取值。

4.一旦写入,两次出错

一旦诺言SuccessFailure完成了,那么我们要做的就是从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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值