Teimuraz
Teimuraz

Reputation: 9335

Scala Future - for comprehension, mix sync and async

In my method1, I need to call another method2 asynchronously, which returns Option (result1). Than, if result1 is empty, I need to call another method3 asynchronously, but if result1 is NOT empty, I just need to return it.

Here is the method:

  def signIn(username: String): Future[User] = {
    for {
      foundUser <- userService.findByUsername(username) // this method returns Future[Option[User]], 
      // foundUser is Option[User]
      user <- if (foundUser.isEmpty) {
        val newUser = User(username = "User123")
        userService.create(newUser).map(Some(_)) // this method returns Future[Option[User]]
      }
      else
        // Here I want to return just foundUser, of course, it is not possible. 
        // IS THIS APPROACH CORRECT?? DOES THIS LINE CREATE ASYNCHRONOUS CALL?          
        Future.successful(foundUser)
    } yield user 
  }

Question is:

Future.successful(foundUser) - is this approach correct in the code above? Does this line create asynchronous call? If so, how to avoid it? I've already fetched foundUser asynchronously, and I don't want to make additional async call just to return already fetched value.

Upvotes: 5

Views: 851

Answers (2)

Yuval Itzchakov
Yuval Itzchakov

Reputation: 149636

Future.successful doesn't queue an additional function on the provided ExecutionContext. It simply uses a Promise[T] to create a completed Future[T]:

/** Creates an already completed Future with the specified result.
   *
   *  @tparam T       the type of the value in the future
   *  @param result   the given successful value
   *  @return         the newly created `Future` instance
   */
  def successful[T](result: T): Future[T] = Promise.successful(result).future

As a side note, you can reduce the amount of boilerplate using Option.fold:

def signIn(username: String): Future[User] = 
  userService
    .findByUsername(username)
    .flatMap(_.fold(userService.create(User(username = "User123")))(Future.successful(_))

Upvotes: 4

Jean Logeart
Jean Logeart

Reputation: 53869

@Yuval Itzchakov answered your question, but as a side note, you may want to use flatMap with pattern matching directly in your case. I personnally find it more readable:

def signIn(username: String): Future[User] =
  userService.findByUsername(username)
    .flatMap {
      case Some(user) => Future.successful(user)
      case _ => userService.create(User(username = "User123"))
    }

Upvotes: 1

Related Questions