由于Play的工作方式,动作代码必须尽可能快(即非阻塞)。那么如果我们还不能生成它,我们应该返回什么呢?回应是未来的结果!
未来[结果]最终将使用Result类型的值进行兑换。通过给出Future [Result]而不是普通Result,我们可以快速生成结果而不会阻塞。然后,Play将在兑换承诺后立即提供此结果。
在等待响应时将阻止Web客户端,但服务器上不会阻止任何内容,服务器资源可用于为其他客户端提供服务。
返回Future的操作创建Action.async
,而不是Action.apply
,用于正常的非异步操作。
使用非异步操作有什么好处吗?令我感到震惊的是,确保我的所有操作都不会阻止的最佳方法是使用Action.async
声明所有操作。
事实上,根据Play {2}中的Play Framework 2.3 documentation,所有操作都是异步的:
注意:Action.apply和Action.async都会以相同的方式创建内部处理的Action对象。有一种Action是异步的,而不是两种(同步的和异步的)。 .async构建器只是简化基于返回Future的API创建操作的工具,这使得编写非阻塞代码变得更加容易。
答案 0 :(得分:12)
仅仅因为您可能使用Action.async
,并不自动意味着您没有阻止。这完全取决于您是否使用阻止API。
Play 2.2似乎与Play 2.3的工作方式相同。除了签名之外,Action.apply
和Action.async
之间并没有什么区别。 Action.async
需要一些返回Future[Result]
的代码块,而Action.apply
需要一段返回Result
的代码。 Action.apply
只需拨打block: => Result
即可将Future[Result]
转换为Future.successful(block)
。 (在调用Future.successful
之前还有一些工作要做,但这是它的要点。)
因此,每个用例都归结为您正在使用的API。例如JDBC vs ScalikeJDBC-async,阻塞与非阻塞数据库API。假设您从数据库中获取用户并将其作为json发送回客户端。
典型的JDBC支持函数的签名可能如下所示(忽略简化失败):
def read(id: Long): User
您的控制器功能可能如下所示:
def read(id: Long) = Action {
Ok(Json.toJson(User.read(id))
}
这大致相当于Action.apply
的作用:
def read(id: Long) = Action.async {
Future.successful(Ok(Json.toJson(User.read(id)))
}
User.read
仍然是一个阻塞的JDBC调用,所以这并不比以前更好。
现在假设我们正在使用看起来像这样的异步数据库调用:
def read(id: Long): Future[User]
控制器功能看起来像这样:
def read(id: Long) = Action.async {
User.read(id).map(user => Ok(Json.toJson(user)))
}
将其视为使用返回Future
的API的助手。真正的好处来自这些API的实际异步实现。如果您遇到阻塞API(可能是JDBC),还有其他方法可以对其进行管理。 Play邮件列表上的这个主题是关于这个主题的好读物:https://groups.google.com/forum/#!topic/play-framework/WWQ0HeLDOjg