Scala Future — для понимания, совмещения синхронизации и асинхронности

В моем методе1 мне нужно асинхронно вызвать другой метод2, который возвращает Option (результат1). Затем, если результат1 пуст, мне нужно асинхронно вызвать другой метод3, но если результат1 НЕ пуст, мне просто нужно вернуть его.

Вот метод:

  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 
  }

Вопрос:

Future.successful(foundUser) - правильный ли подход в приведенном выше коде? Эта строка создает асинхронный вызов? Если да, то как этого избежать? Я уже получил foundUser асинхронно и не хочу делать дополнительный асинхронный вызов только для возврата уже полученного значения.


person Teimuraz    schedule 22.08.2016    source источник


Ответы (2)


Future.successful не ставит в очередь дополнительную функцию для предоставленного ExecutionContext. Он просто использует Promise[T] для создания завершенного 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

В качестве примечания, вы можете уменьшить количество шаблонов, используя Option.fold:

def signIn(username: String): Future[User] = 
  userService
    .findByUsername(username)
    .flatMap(_.fold(userService.create(User(username = "User123")))(Future.successful(_))
person Yuval Itzchakov    schedule 22.08.2016
comment
кстати, если я пишу Future {foundUser} вместо Future.successful(foundUser), то он выполняется асинхронно, верно? - person Teimuraz; 22.08.2016
comment
@moreo Да, это вызовет очередь функции на ExecutionContext. - person Yuval Itzchakov; 22.08.2016

@Yuval Itzchakov ответил на ваш вопрос, но в качестве примечания вы можете использовать flatMap с сопоставлением с образцом непосредственно в вашем случае. Я лично нахожу это более читаемым:

def signIn(username: String): Future[User] =
  userService.findByUsername(username)
    .flatMap {
      case Some(user) => Future.successful(user)
      case _ => userService.create(User(username = "User123"))
    }
person Jean Logeart    schedule 22.08.2016
comment
Спасибо, сначала я реализовал его с помощью flatMap, но мой пример здесь очень упрощен по сравнению с моим первоначальным методом, у меня было много вложенных плоских карт/карт, поэтому я решил переписать его для понимания. - person Teimuraz; 22.08.2016