Работа с будущими типами в Scala

Я немного изо всех сил, чтобы получить это структурировано. Вот что я пытаюсь сделать:

def checkResultAndFetchUser(result: WriteResult, encryptedEmail: String): Future[Either[ServiceError, User]] = Future {
  if (result.code contains 11000)
    Left(ServiceError("Email already exists"))
  else if (result.hasErrors)
    Left(ServiceError(result.writeErrors.map(_.errmsg).toString))
  else
    userByEmail(encryptedEmail).map(user =>
      user
    ).recover {
      case NonFatal(ex) => Left(ServiceError(ex.getMessage))
    }
}

checkResultAndFetchUser(
  await(userCollection.insert(encryptedUser)), encryptedUser.email
)

Я ожидаю, что checkResultAndFetchUser возвращает Future[Either[ServiceError, User]], но я вижу следующие ошибки компилятора:

Error:(155, 28) type mismatch;
 found   : scala.concurrent.Future[Either[DBService.this.ServiceError,com.inland.model.User]]
 required: Either[DBService.this.ServiceError,com.inland.model.User]
Error occurred in an application involving default arguments.
    checkResultAndFetchUser(
                           ^
Error:(150, 19) type mismatch;
 found   : scala.concurrent.Future[Either[DBService.this.ServiceError,com.inland.model.User]]
 required: Either[DBService.this.ServiceError,com.inland.model.User]
        ).recover {
                  ^

userByEmail(encryptedEmail) метод дает мне Future[Either[ServiceError, User]] как я и ожидал, но почему и где проблема?

РЕДАКТИРОВАТЬ: Я нашел решение:

def checkResultAndFetchUser(result: WriteResult, encryptedEmail: String): Future[Either[ServiceError, User]] = {
  if (result.code contains 11000)
    Future(Left(ServiceError("Email already exists")))
  else if (result.hasErrors)
    Future(Left(ServiceError(result.writeErrors.map(_.errmsg).toString)))
  else
    userByEmail(encryptedEmail)
}

await(checkResultAndFetchUser(
  await(userCollection.insert(encryptedUser)), encryptedUser.email
))

Все хорошо? Я имею в виду, что реализация безопасна, так как я использую локальные переменные для возврата Future!

1 ответ

Решение

Ваш код в порядке в том смысле, что он дает ожидаемый результат. Однако, как упомянул @ @ukasz в комментарии, делать это таким образом немного расточительно.

Причина в том, что всякий раз, когда вы создаете экземпляр Future, создается новое задание, которое должно быть запланировано для некоторого ExecutionContext. Обычно, когда вам просто нужно обернуть уже вычисленный результат в Future (или, если вычисления действительно быстрые), лучше использовать Future.successful так, чтобы избежать накладных расходов.

Вот как бы я изменил функцию checkResultAndFetchUser:

def checkResultAndFetchUser(result: WriteResult, encryptedEmail: String): Future[Either[ServiceError, User]] = {
  if (result.code contains 11000)
    Future.successful(Left(ServiceError("Email already exists")))
  else if (result.hasErrors)
    Future.successful(Left(ServiceError(result.writeErrors.map(_.errmsg).toString)))
  else
    userByEmail(encryptedEmail)
}
Другие вопросы по тегам