简体   繁体   中英

Scala work with multiple futures

I have two source of data which returns List[Int]

// first work with postgresql database

object Db {

  def get: Future[List[Int]] = // impl

}
// second it's remote service
object Rs {
  def get: Future[List[Int]] = // impl
}

And then i want to return two lists. But i do not know how to dealing with exceptions:

  1. Db possible throw ConnectionUnavailable

  2. Remote service - Bad Request, or InternalServer error

  3. Both - TimeoutException

But, when i have only results from db, i want to return it. If i have results from db and from remote service i want to return sum of two lists.

How to work with this case?

val total: Future[Int] =
  Db.get.flatMap { dbResults =>
    Rs.get.map { remoteResults =>
      dbResults.sum + remoteResults.sum
    }
  }

or equivalently

   val total: Future[Int] = for {
     dbResults <- Db.get
     remoteResults <- Rs.get
   } yield dbResults.sum + remoteResults.sum

I explicitly annotated the result type for the sake of clarity but it's not necessary.

total is a Future[Int] holding either a successful or a failed computation. If you need to handle errors you can attach a onFailure handler on it. For instance

total.onFailure {
  case e: TimeoutException => // ...
  case e: ConnectionError  => // ...
}

(the names of the exceptions are made up)

You need to combine flatMap and recover :

for {
  db <- Db.get
  rs <- Rs.get.recover { 
    case e =>
      logger.error("Error requesting external service", e)
      List.fill(db.length)(0)
  }
} yield (db, rs).zipped.map(_+_).sum

You can tweak the transformations if you like (I assumed that you meant element-wise sum of lists), but the basic idea stays the same - if you want to "ignore" the failure of some future, you need to call recover on it.

If you want, you can extract the recovering function from the for comprehension, but recover still has to be called inside of it:

def handler(n: Int): PartialFunction[Throwable, List[Int]] = {
  case e =>
    logger.error("Error requesting external service", e)
    List.fill(n)(0)
}

for {
  db <- Db.get
  rs <- Rs.get.recover(handler(db.length))
} yield (db, rs).zipped.map(_+_).sum

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM