首先,Scalaz Task 是惰性的:它不会在您创建它或从某种方法获取时启动。这是设计使然,允许Tasks 很好地结合。在您将整个程序完全组装到单个主Task 之前,它不会开始,因此当您完成后,您需要使用perform 方法之一手动启动聚合Task,例如unsafePerformSync将等待当前线程上聚合异步计算的结果:
(for {
foo <- println("we are in this for loop!").asAsyncSuccess()
authenticatedUser <- methodA(parameterA)
foo2 <- println("we are out of this this call!").asAsyncSuccess()
} yield {
authenticatedUser
}).unsafePerformSync
从原始代码中可以看出,您从未启动过任何Task。但是,您提到第一个 println 将其消息打印到控制台。这可能与 asAsyncSuccess 方法有关:我在 Scalaz 中没有找到它,所以我假设它在您代码中某处的隐式类中:
implicit class TaskOps[A](val a: A) extends AnyVal {
def asAsyncSuccess(): Task[A] = Task(a)
}
如果你像这样编写一个辅助隐式类,它不会将有效的表达式转换为懒惰的Task 动作,尽管Task.apply
是call-by-name。它只会转换它的结果,恰好是Unit,因为它自己的参数a 不是按名称调用的。要按预期工作,它需要如下所示:
implicit class TaskOps[A](a: => A) {
def asAsyncSuccess(): Task[A] = Task(a)
}
您可能还会问为什么第一个 println 打印了一些东西,而第二个没有。这与for-comprehensions desugaring 进入方法调用有关。具体来说,编译器会将你的代码变成这样:
println("we are in this for loop!").asAsyncSuccess().flatMap(foo =>
methodA(parameterA).flatMap(authenticatedUser =>
println("we are out of this this call!").asAsyncSuccess().map(foo2 =>
authenticatedUser)))
如果asAsyncSuccess 不尊重预期的Task 语义,则首先会立即执行println。但是它的结果随后被包装到 Task 中,也就是说,本质上只是一个对 start 函数的包装,而 flatMap 的实现只是将这些函数组合在一起而不运行它们。因此,methodA 或第二个 println 将不会被执行,直到您调用 unsafePerformSync 或其他 perform 辅助方法。但是请注意,第二个println 仍然会比它应该根据Task 语义更早地执行,而不是更早那个。