在 Scala 中处理 Future[Either] 类型
Dealing with Future[Either] Types in 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
return 是 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
))
可以吗?我的意思是,实现是安全的,因为我使用局部变量 return a Future
!
从产生预期结果的意义上说,您的代码没有问题。然而正如@Ł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)
}
我有点难以理解这个结构。这是我正在尝试做的事情:
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
return 是 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
))
可以吗?我的意思是,实现是安全的,因为我使用局部变量 return a Future
!
从产生预期结果的意义上说,您的代码没有问题。然而正如@Ł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)
}