简体   繁体   中英

How to validate single element in Future[List[T]] to return Future[List[T]] or throw exception

No need to pay attention to the purpose of the function here, it's only for demonstration:

def readAllByPersonOrFail(person: Person, otherPersonId: Long): Future[List[Person]] = {
  val personSiblingsFuture: Future[List[Person]] = personSiblingsDomain.readAllByPersonId(person.id)
  personSiblingsFuture.map { persons =>
    persons.find(_.id == otherPersonId) match {
      case Some(person) =>
        person.isActive match {
          case true => person
          case false => throw new IllegalArgumentException("something inactive")
        }
      case None => throw new IllegalArgumentException("something wrong ehre")
    }
  }
  personSiblingsFuture
}

I would like to return personSiblingsFuture above iff it validates (makes sure correct person is in the list and is active), otherwise throw the exception. I don't think the above code is doing the right thing as it is not existing upon failure.

Take a look at scala.concurrent.Future.map . This creates a new future, whose value is resolved by applying a function to the successful result of this future.

Note that here you're throwing away the resulting future you just created with .map() too.

There are a few areas to solve your problem, though you should question more deeply the use of exceptions with Futures . Scala provides concepts like Future , Option , and Try specifically to avoid throwing exceptions and have a clearer control flow.

Option 1, return the mapped future

In your funciton,

def func(...): Future[List[Person]] {
  val personSiblingsFuture = ...;
  personSiblingsFuture.map { persons =>
    ...
  }
}
// note we're not returning personSiblingsFuture,
// but the mapped result

When someone actually tries to get the value of the future, eg by using .value , they might see an exception intead:

def main() {
  val future = func(...);  // this is fine
  val my_list = future.value;  // awaits Future, might throw here
}

Option 2, actually await the list and throw in the function

Returning a future that might throw is strange, it might be a bit easier if the you actually explicitly a had a function that might throw, eg

/** jsdoc describing function **/
def funcMightThrow(...): List[Person] {
  val personSiblingsFuture = ...;
  val personSiblings = personSiblingsFuture.value;

  personSiblings.find(_.id == otherPersonId) match {
    case Some(person) =>
      person.isActive match {
        case true => personSiblings
        case false => throw new IllegalArgumentException("something inactive")
      }
    case None => throw new IllegalArgumentException("something wrong ehre")
  }
}

Option 3, consider making return types more explicit

def func(...): Future[Try[List[Person]]] {
  val personSiblingsFuture = ...;
  personSiblingsFuture.map { persons =>
    ...
    // successful case returns 'persons' (List[Person])
    // fail cases return Failure(...) instead
  }
}  // return the mapped future

You can also return Try[List[Person]] rather than a Future[] of that, by using .value , which makes func a blocking function.

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