显示导航

Grails 承诺

了解如何使用 Grails 承诺和并行加载多个 REST 有效负载。

作者:Sergio del Amo

Grails 版本 5.0.1

1 Grails 培训

Grails 培训 - 由创建并积极维护 Grails 框架的人员开发和交付!

2 开始

2.1 你会需要

要完成本指南,你需要以下内容

  • 一些时间

  • 一个像样的文本编辑器或 IDE

  • 安装了 JDK 1.8 或更高版本,且已适当地配置了 JAVA_HOME

2.2 如何完成此指南

要开始,请执行以下操作

Grails 指南存储库包含两个文件夹

  • initial 初始项目。通常是一个简单的 Grails 应用程序,其中包含一些附加代码,以便帮助你快速入门。

  • complete 完成的示例。这是按照指南提供的步骤操作并在 initial 文件夹中应用这些更改的结果。

要完成指南,请转到 initial 文件夹

  • cdgrails-guides/grails-async-promises/initial

并按照下一部分中的说明进行操作。

如果你 cdgrails-guides/grails-async-promises/complete,则可以直接转到完整示例

2.3 初始项目

在 Grails 指南(使用并测试第三方 REST API)中开发的示例 Grails 应用程序的初始文件夹包含此部分。

其中包含用于调用 Open Weather Map API 以检索某个城市的预报的天气代码。

OpenWeathermapService.currentWeather 执行网络请求并使用收到的 json 有效负载构建一个对象。

目标是显示预测网格

cities

3 编写应用程序

添加了 Grails 2.3,并且自 3.3 起,Grails 的一个外部项目,Async 功能旨在简化框架内的并发编程,并包括 Promises 概念和一个统一的事件模型。

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

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

可通过 async.grails.org 获得 Grails Async 功能力文档。

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 grails.async.Promise
import grails.async.PromiseList

OpenweathermapService 添加以下方法

grails-app/services/org/openweathermap/OpenweathermapService.groovy
    @CompileDynamic
    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 {

    public static final String US = 'us'
    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 渲染天气预报网格

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 的家

认识团队