Handling futures with for-comp, but if clauses are

2019-08-07 07:11发布

问题:

I find that I am running into the below pattern allot and I can't get it to work or look nice style wise.

I have a for comprehension that is return Futures, and then I build my model to display in a view. But before I return the Result in my Action, I sometimes have to branch using an if clause and potentially load more data.

The below code doesn't compile currently, what do you suggest I do to make the below code follow correct style with this type of pattern?

It doesn't compile because the inner for compr is return a Future[Option[Something]] but I have defined c as Option[Something]

for {
  a <- fooService.getA()
  b <- fooService.getB()
} yield {
  var c: Option[Something] = None
  if(a.size > 0) {
    c = for {
      c <- fooService.getC()
    } yield {
      Some(c)
    }
  }
}
val model = FooModel(a, b, c)
Ok(views.html.foo.show(model))

My view model is defined as:

FooModel(a: A, b: B, c: Option[Something])

回答1:

This seems reasonably clear:

for {
  a <- fooService.getA()
  b <- fooService.getB()
  c <- if (a.nonEmpty) fooService.getC() else Future.successful(None)
} yield {
  val model = FooModel(a, b, c)
  Ok(views.html.foo.show(model))
}

Extract the if (a.nonEmpty) fooService.getC() else Future.successful(None) to another method or service if you want.



回答2:

Now it compiles, but have you noticed, that your program runs sequentially?

object fooService{
  def getA() = {
    Future{
      println("I'm getA")
      for(i <- 1 to 10){
        println(".")
        Thread.sleep(200)
      }
      "A"
    }
  }
  def getB() = {
    Future{
      println("I'm getB")
      for(i <- 1 to 10){
        println(".")
        Thread.sleep(200)
      }
      "B"
    }
  }
}

object Main {
  def main(args: Array[String]) ={
    for {
      a <- fooService.getA()
      b <- fooService.getB()
    } println(a + b)

    Thread.sleep(4000)
  }
}

This is the output:

I'm getA
.
.
.
.
.
.
.
.
.
.
I'm getB
.
.
.
.
.
.
.
.
.
.
AB

And with a little change:

object fooService{
  def getA() = {
    Future{
      println("I'm getA")
      for(i <- 1 to 10){
        println(".")
        Thread.sleep(200)
      }
      "A"
    }
  }
  def getB() = {
    Future{
      println("I'm getB")
      for(i <- 1 to 10){
        println(".")
        Thread.sleep(200)
      }
      "B"
    }
  }
}

object Main {
  def main(args: Array[String]) ={
    val f1 = fooService.getA()
    val f2 = fooService.getB()
    for {
      a <- f1
      b <- f2
    } println(a + b)

    Thread.sleep(4000)
  }
}

Output:

I'm getA
I'm getB
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
AB