【问题标题】:Dealing with Closures and Futures in Scala在 Scala 中处理闭包和期货
【发布时间】:2016-01-24 10:29:28
【问题描述】:

我有以下方法可以对Futures 进行一些处理:

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))
}

这是我的问题:

  1. checkResultAndFetchUser 方法中使用了encryptedUser 局部变量,userCollection.insert(encryptedUser) 返回一个Future,因此有可能在checkResultAndFetchUser 中捕获另一个encryptedUser方法?那意味着,我注定要失败!

  2. 这是处理Futures 及其处理的好方法吗?

【问题讨论】:

    标签: scala closures future


    【解决方案1】:

    我不太确定你的第一个问题是什么意思,但只有一个 encryptedUser 值,所以即使两个函数都返回 Future,它们都使用相同的 encryptedUser

    为了处理你Futures:

    • 您并没有真正使用 async / await,因为您只是在“结果”Future 上使用了await
    • 你在userCollection.insert()上使用map,然后使用result.flatMap(fut => fut),可以简化为直接使用flatMap

      userCollection.insert(encryptedUser).flatMap( writeRes =>
        checkResultAndFetchUser(writeRes, encryptedUser.email)
      )
      
    • 这也可以写成一种理解。

    最终结果可能如下所示:

    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
    }
    

    【讨论】:

    • 我的意思是让多个线程调用 registerNewUser 方法会有什么影响? thread1 中的 encryptedUser 会在 encryptedUser2 的线程中使用吗?我上面的代码结构会不会出现这种情况?
    • 每次你调用registerNewUser这个函数都有它自己的encryptedUser,但我认为encrypt(user)对于相同的输入返回相同的结果user
    • 是的,加密为相同的输入用户返回相同的结果!这不是我关心的问题。当我关闭一个局部变量时,当关闭的变量将在另一个线程中执行时我应该小心。我希望我的实现是安全的!
    • 如果User 是一个不可变的案例类(它可能应该),那没关系,因为您不能在任何线程中更改encryptedUser
    • 我的问题是 Future userCollection.insert(user) 的执行是否发生在与 checkResultAndFetchUser 方法不同的线程中?在这种情况下, checkResultAndFetchUser 会看到另一个 encryptedUser 或?
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2017-09-30
    • 2014-01-10
    • 1970-01-01
    • 1970-01-01
    • 2017-03-26
    • 1970-01-01
    • 2015-07-17
    相关资源
    最近更新 更多