显示导航

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 如何完成本指南

要开始,请执行以下操作

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 对象。

目标是展示一个预测网格

cities

3 编写应用程序

添加了 Grails 2.3 以及 3.3 及更高版本的一个外部项目,Grails 的异步功能旨在简化框架内的并发编程,包含了 Promise 和统一事件模型的概念。

您的项目已经包含了 async 依赖项

build.gradle
    compile "org.grails.plugins:async"

可以在 async.grails.org 中找到 Grails 异步能力的文档。

3.1 主要城市

添加一个包含美国最大城市的类。

src/main/groovy/demo/LargestUSCities.groovy
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 的入口

grails-app/services/org/openweathermap/OpenweathermapService.groovy
import static grails.async.Promises.*

将以下方法添加到 OpenweathermapService

grails-app/services/org/openweathermap/OpenweathermapService.groovy
    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 实例的并集

添加一个等效的同步方法。

grails-app/services/org/openweathermap/OpenweathermapService.groovy
    List<CurrentWeather> findCurrentWeatherByCitiesAndCountryCode(List<String> cities, String countryCode, Unit unit) {
        cities.collect { currentWeather(it, countryCode, unit) }
    }

3.3 城市控制器

创建一个使用前一个服务方法的名为 CitiesController 的控制器

grails-app/controllers/demo/CitiesController.groovy
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 如果 asyncfalse,则同步创建模型。

3.4 视图

使用 GSP 渲染天气预报网格

grails-app/views/cities/index.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 的异步能力,请访问

runasync

访问 http://localhost:8080/cities?async=false 以同步获取天气预报。

runsync

5 Grails 帮助

Object Computing, Inc. (OCI) 赞助了本指南的创建。我们提供各种咨询和支持服务。

OCI是Grails的归宿

认识团队