简体   繁体   中英

Dealing with Closures and Futures in Scala

I have the following method that does some handling with Future s:

def registerNewUser(user: User): Future[Either[ServiceError, User]] = async {
  val encryptedUser =
    user.copy(encrypt(user))

  def checkResultAndFetchUser(result: WriteResult, encryptedEmail: String): Future[Either[ServiceError, User]] = {
    if (result.hasErrors)
      Future.successful(Left(ServiceError(result.writeErrors.map(_.errmsg).toString)))
    else
      userByEmail(encryptedEmail)
  }

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

  val result = userCollection.insert(encryptedUser).map( writeRes =>
    checkResultAndFetchUser(writeRes, encryptedUser.email)
  ).recover {
    case res: WriteResult => Future.successful(handleInsertError(res))
  }

  await(result.flatMap(fut => fut))
}

Here are my questions:

  1. The encryptedUser local variable is being used in the checkResultAndFetchUser method, the userCollection.insert(encryptedUser) returns a Future , so is there any chance that another encryptedUser might be captured in the checkResultAndFetchUser method? That would mean, I'm doomed!

  2. Is this a good way of dealing with Future s and its handling?

I'm not exactly sure what you mean with your first question, but there is only one encryptedUser value, so even though both functions return a Future they both use the same encryptedUser .

For handling you Future s :

  • You aren't really using async / await, since you are only using await on the "result" Future .
  • You are using map on userCollection.insert() and then using result.flatMap(fut => fut) , which can be simplified to using flatMap directly.

     userCollection.insert(encryptedUser).flatMap( writeRes => checkResultAndFetchUser(writeRes, encryptedUser.email) ) 
  • Which could also be written as a for comprehension.

The end result could look like :

def registerNewUser(user: User): Future[Either[ServiceError, User]] = { 
  def checkResultAndFetchUser(result: WriteResult, encryptedEmail: String) = ???

  def handleInsertError(result: WriteResult) = ???

  def insertUser(user: User): WriteResult =
    userCollection.insert(user).recover {
      case res: WriteResult => handleInsertError(res)
    }

  val encryptedUser = user.copy(encrypt(user))

  for {
    writeRes <- insertUser(encryptedUser)
    user     <- checkResultAndFetchUser(writeRes, encryptedUser.email)
  } yield user
}

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