Grails 承诺
了解如何使用 Grails 承诺并并行加载多个 REST 有效负载。
作者:Sergio del Amo
Grails 版本 3.3.1
1 Grails 培训
Grails 培训 - 由创建和积极维护 Grails 框架的人员开发和提供!
2 入门
2.1 你需要什么
要完成本指南,你需要
-
一些时间
-
一个不错的文本编辑器或 IDE
-
安装 JDK 1.7 或更高版本,并相应配置好
JAVA_HOME
2.2 如何完成本指南
要开始,请执行以下操作
-
下载并解压源代码
或
-
克隆 Git 存储库
git clone https://github.com/grails-guides/grails-async-promises.git
Grails 指南存储库包含两个文件夹
-
initial
初始项目。通常是一个简单的 Grails 应用程序,其中包含一些附加代码,让你可以抢先一步。 -
complete
完成的示例。它是按照指南提供步骤操作并对initial
文件夹应用这些更改的结果。
要完成指南,请转到 initial
文件夹
-
cd
进入grails-guides/grails-async-promises/initial
并按照下一部分中的说明操作。
如果你 cd 进入 grails-guides/grails-async-promises/complete ,则可以直接转到已完成示例。 |
2.3 初始项目
在 Grails 指南期间开发的样本 Grails 应用程序initial 文件夹包含:使用并测试第三方 REST API。
它包含调用 Open Weather Map API 以检索城市的天气预报的代码。
OpenWeathermapService.currentWeather
执行网络请求,并使用收到的 JSON Payload 生成一个 Groovy 对象。
目标是展示一个预测网格
3 编写应用程序
添加了 Grails 2.3 以及 3.3 及更高版本的一个外部项目,Grails 的异步功能旨在简化框架内的并发编程,包含了 Promise 和统一事件模型的概念。
您的项目已经包含了 async 依赖项
compile "org.grails.plugins:async"
可以在 async.grails.org 中找到 Grails 异步能力的文档。
3.1 主要城市
添加一个包含美国最大城市的类。
package demo
import groovy.transform.CompileStatic
@CompileStatic
class LargestUSCities {
public static final List<String> CITIES = [
'New York City',
'Los Angeles',
'Chicago',
'Houston',
'Philadelphia',
'Phoenix',
'San Antonio',
'San Diego',
'Dallas',
'San Jose',
'Austin',
'Jacksonville',
'Indianapolis',
'San Francisco',
'Columbus',
'Fort Worth',
'Charlotte',
'Detroit',
'El Paso',
'Memphis',
'Boston',
'Seattle',
'Denver',
'Washington',
'Nashville-Davidson',
'Baltimore',
'Louisville/Jefferson',
'Portland',
'Oklahoma',
'Milwaukee',
'Las Vegas',
'Albuquerque',
'Tucson',
'Fresno',
'Sacramento',
'Long Beach',
'Kansas',
'Mesa',
'Virginia Beach',
'Atlanta',
'Colorado Springs',
'Raleigh',
'Omaha',
'Miami',
'Oakland',
'Tulsa',
'Minneapolis',
'Cleveland',
'Wichita',
'Arlington',
'New Orleans',
'Bakersfield',
'Tampa',
'Honolulu',
'Anaheim',
'Aurora',
'Santa Ana',
'St. Louis',
'Riverside',
'Corpus Christi',
'Pittsburgh',
'Lexington-Fayette',
'Stockton',
'Cincinnati',
'St. Paul',
'Toledo',
'Newark',
'Greensboro',
'Plano',
'Henderson',
'Lincoln',
'Buffalo',
'Fort Wayne',
'Jersey',
'Chula Vista',
'Orlando',
'St. Petersburg',
'Norfolk',
'Chandler',
'Laredo',
'Madison',
'Durham',
'Lubbock',
'Winston-Salem',
'Garland',
'Glendale',
'Hialeah',
'Reno',
'Baton Rouge',
'Irvine',
'Chesapeake',
'Irving',
'Scottsdale',
'North Las Vegas',
'Fremont',
'San Bernardino',
'Boise',
'Birmingham',
]
}
3.2 Open Weather Service
Promise 是许多并发框架采用的一个概念。它们与 java.util.concurrent.Future 实例类似,但包含了一个更友好的异常处理模型、诸如链条以及附加侦听器能力等有用的功能。
在 Grails 中,grails.async.Promises
类提供了一个进入 Promise API 的入口
import static grails.async.Promises.*
将以下方法添加到 OpenweathermapService
Promise<List<CurrentWeather>> findCurrentWeatherByCitiesAndCountryCodeWithPromises(List<String> cities, String countryCode, Unit unit) {
PromiseList<CurrentWeather> list = new PromiseList<CurrentWeather>()
cities.each { String city ->
list << task { (1)
currentWeather(city, countryCode, unit)
}
}
return list (2)
}
1 | task 方法,它返回一个 grails.async.Promise 实例。 |
2 | 返回了一个 PromiseList ,其中包含所有已创建 grails.async.Promise 实例的并集 |
添加一个等效的同步方法。
List<CurrentWeather> findCurrentWeatherByCitiesAndCountryCode(List<String> cities, String countryCode, Unit unit) {
cities.collect { currentWeather(it, countryCode, unit) }
}
3.3 城市控制器
创建一个使用前一个服务方法的名为 CitiesController
的控制器
package demo
import grails.async.Promise
import static grails.async.Promises.*
import groovy.transform.CompileStatic
import org.openweathermap.CurrentWeather
import org.openweathermap.OpenweathermapService
import org.openweathermap.Unit
@CompileStatic
class CitiesController {
OpenweathermapService openweathermapService
def index(String unit, boolean async) {
Unit unitEnum = Unit.unitWithString(unit)
if ( async ) { (1)
Promise<List<CurrentWeather>> currentWeatherList = openweathermapService.findCurrentWeatherByCitiesAndCountryCodeWithPromises(LargestUSCities.CITIES, 'us', unitEnum)
return tasks( (2)
currentWeatherList: currentWeatherList,
unit: createBoundPromise(unitEnum)
)
} else { (3)
List<CurrentWeather> currentWeatherList = openweathermapService.findCurrentWeatherByCitiesAndCountryCode(LargestUSCities.CITIES, 'us', unitEnum)
return [currentWeatherList: currentWeatherList, unit: unitEnum]
}
}
}
1 | 如果 async 参数为 true ,则使用先前创建的返回 promise 的服务方法。 |
2 | 返回一组已命名任务,其中每个键成为视图模型中的已解析值。Grails 会检测到返回了一个 promise,并创建一个非阻塞响应。createBoundPromise 方法用于定义一个已经绑定且不需要异步解析的 Promise 。 |
3 | 如果 async 为 false ,则同步创建模型。 |
3.4 视图
使用 GSP 渲染天气预报网格
<html>
<head>
<title>Largest US City Weather</title>
<meta name="layout" content="main" />
</head>
<body>
<div id="content" role="main">
<section class="row colset-2-its">
<g:each in="${currentWeatherList}" var="${currentWeather}">
<g:if test="${currentWeather}">
<g:render template="/openweather/currentWeather"
model="[currentWeather: currentWeather, unit: unit]" />
</g:if>
</g:each>
</section>
</div>
</body>
</html>
4 运行应用程序
要运行应用程序,请使用 ./gradlew bootRun
命令,该命令将在 8080 端口上启动应用程序。
请记住在 application.yml
中设置一个有效的 Open Weather Map API 密钥
要获取美国最大城市的天气预报,利用 Grails 的异步能力,请访问
访问 http://localhost:8080/cities?async=false
以同步获取天气预报。