2.6 zio入门——对比Future

2.6 对比Future

本小节,我们可以通过比较Scala标准库中的ZIO和Future来阐明到目前为止所学的知识。
在本书后面讨论并发性时,我们将讨论ZIO和Future之间的其他差异,但目前要记住三个主要差异。

2.6.1 一个 Future 就是一个正在运行的 effect

与ZIO之类的函数式作用不同,Future是对运行时的作用进行建模。 回到先前的一个示例,请考虑以下代码段:

  import scala.concurrent.Future
  import scala.concurrent.ExecutionContext.Implicits.global
  val goShopping: Future[Unit] = Future(println("Going to the grocery store"))

就像我们最初的示例一样,一旦定义goShopping,此效果就会开始执行。 Future不会暂停对其中包装的代码的运行。
由于"做什么"与"怎么做"之间存在纠结,在Future显得并不是那么强大。例如,就像在ZIO上一样,能够在Future上定义一个延迟运算符会很好。但是我们不能这样做,因为一个Future一旦存在,它就已经开始运行,没有机会再延迟计算。
同样,在失败的情况下我们也不能像ZIO那样重试Future,因为Future并不是做某事的蓝计划,而是一种执行性的计算。因此,如果Future失败,则无事可做。我们只能检索失败。
相比之下,由于ZIO effect是并发工作流程的蓝图,因此,如果执行一次该效果但失败了,我们可以随时尝试再次执行它,或者执行任意多次。
这种区别特别明显的一种情况是,每当您在Future上调用方法时,您就必须在范围内具有隐式ExecutionContext。

例如,这是Future#flatMap的签名,与ZIO上的flatMap一样,它使我们可以编写顺序效果:

  import scala.concurrent.ExecutionContext
  trait Future[+A] {
    def flatMap[B](f: A => Future[B])(implicit ec: ExecutionContext): Future[B]
  }

Future#flatMap需要一个ExecutionContext,因为它表示一个运行中的effect,因此我们需要提供ExecutionContext,随后的代码应在该ExecutionContext上立即运行。
如前所述,这将"要完成的工作"与"如何完成的工作"混为一谈。 相反,我们所见的涉及ZIO的代码都不需要执行程序,因为它只是一个蓝图。
ZIO蓝图可以在我们想要的任何Executor上运行,但是在实际运行效果之前,不必指定它(或者稍后,我们将看到如何 “lock” 效果以在特定 execution context 中运行,那些罕见的情况
您需要对此明确说明)。

2.6.2 Future的错误类型固定为Throwable

Future的错误类型固定为Throwable。 我们可以在Future#onComplete的签名中看到这一点:

  import scala.util.Try
  trait Future[+A] {
    def onComplete[B](f: Try[A] => B): Unit
  }

Future的结果可以是成功返回一个A,也可以是抛出Throwable的失败。 当使用可能因任何Throwable失败而遗留的旧代码时,这可能很方便,但与多态错误类型相比,它的表达能力要低得多。
首先,通过查看类型签名,我们不知道effect如何失败或甚至是否会失败。 考虑一下我们讨论用Future实现的ZIO错误类型时所看的乘法示例:

def parseInt: Future[Int] = ???

注意,由于Future是运行时的effect,因此我们必须将其定义为def而不是val。因此,如果我们将其定义为val,我们将立即读取并解析用户的输入。然后,当我们使用parseInt时,我们总是会获得相同的值,而不是提示用户输入新值并进行解析。
抛开这一点,我们不知道通过查看类型签名,未来会如何失败。它可以从解析中返回NumberFormatException吗?它可以返回IOException吗?它会因为自己处理错误而完全失败,也许是通过重试直到用户输入有效的整数为止?我们只是不知道,除非我们深入研究代码并进行深入研究。
这对于调用此方法的开发人员来说更加困难,因为他们不知道会发生哪种类型的错误,因此为了安全起见,他们需要进行“防御性编程”并处理所有可能的Throwable。
当我们处理Future的所有可能的失败方案时,此问题尤其令人讨厌。
例如,我们可以使用Future方法fallbackTo处理parseInt错误:

  import scala.concurrent.Future
  def parseIntOrZero: Future[Int] = parseInt.fallBackTo(Future.successful(0))

这里parseIntOrZero不会失败,因为如果parseInt失败,我们将其替换为成功结果0。但是类型签名并没有告诉我们。 就类型签名而言,此方法可能会无限多种方式失败,就像parseInt!一样!
从编译器的角度来看,fallBackTo并没有改变Future的易错性。 相反,在ZIO中,parseInt将具有IO [NumberFormatException,Int]类型,而parseIntOrZero将具有UIO [Int]类型,从而精确地指示parseInt如何失败以及parseIntOrZero无法失败。

2.6.3 Future 没有办法来对 effect 的依赖关系来建模

到目前为止,我们看到的ZIO和Future之间的最终区别是Future没有任何方法可以对effect的依赖性进行建模。 这需要其他解决方案来进行依赖注入,这些解决方案通常是手动的(无法推断),或者依赖于第三方库。
在本书的后面,我们将花更多的时间在此上,但是现在仅需注意ZIO直接支持依赖项注入,而Future没有。 这意味着在实践中,现实世界中的大多数Future代码并不是可测试的,因为它需要太多的管道和样板。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值