9 非同期プログラミング - Reference Documentation
Authors: Graeme Rocher, Peter Ledbrook, Marc Palmer, Jeff Brown, Luke Daley, Burt Beckwith
Version: 2.4.0.M1
Translated by: T.Yamamoto, Japanese Grails Doc Translating Team. Special thanks to NTT Software.
【注意】このドキュメントの内容はスナップショットバージョンを元に*意訳*されているため、一部現行バージョンでは未対応の機能もあります。
9 非同期プログラミング
9.1 Promises
java.util.concurrent.Future
instances, but include a more user friendly exception handling model, useful features like chaining and the ability to attach listeners.
java.util.concurrent.Future
によく似ていますが、より使いやすい例外ハンドリングモデルや、非同期処理の連鎖、リスナーの追加といったさまざまな便利な機能を備えています。Promise Basics
Promiseの基本
grails.async.Promises
class provides the entry point to the Promise API:
grails.async.Promises
クラスを提供しています。import static grails.async.Promises.*
task
method, which returns an instance of the grails.async.Promise
interface:
task
メソッドを使用します。これはgrails.async.Promise
インタフェースのインタンスを返します。def p1 = task { 2 * 2 } def p2 = task { 4 * 4 } def p3 = task { 8 * 8 } assert [4,16,64] == waitAll(p1, p2, p3)
waitAll
method waits synchronously, blocking the current thread, for all of the concurrent tasks to complete and returns the results.
waitAll
メソッドは現在のスレッドをブロックした状態で処理を待ち合わせ、すべての並行タスクが完了した時点で結果を返します。onComplete
method:
onComplete
メソッドを使用します。onComplete([p1,p2,p3]) { List results -> assert [4,16,64] == results }
waitAll
method will throw an exception if an error occurs executing one of the promises. The originating exception will be thrown. The onComplete
method, however, will simply not execute the passed closure if an exception occurs. You can register an onError
listener if you wish to handle exceptions without blocking:
waitAll
メソッドはpromiseのいずれかでエラーが発生した場合に例外をスローします。これは発信元の例外がスローされます。onComplete
メソッドを使用した場合は、単にクロージャが実行されないだけです。もしブロッキングせずに例外を処理したい場合はonError
メソッドを使用してリスナーを登録できます。onError([p1,p2,p3]) { Throwable t ->
println "An error occured ${t.message}"
}
grails.async.Promise
interface provides a similar API on the promise itself. For example:
grails.async.Promise
インターフェース自身で同様のAPIを提供しています。例えば以下のように使用します。import static java.util.concurrent.TimeUnit.* import static grails.async.Promises.*Promise p = task { // Long running task } p.onError { Throwable err -> println "An error occured ${err.message}" } p.onComplete { result -> println "Promise returned $result" } // block until result is called def result = p.get() // block for the specified time def result = p.get(1,MINUTES)
Promise Chaining
Promiseのチェーン
then
method:
then
メソッドを使用してpromiseをチェーンすることで、チェーンの完了を待ち合わせることができます。final polish = { … } final transform = { … } final save = { … } final notify = { … }Promise promise = task { // long running task } promise.then polish then transform then save then { // notify end result }
Promise Lists and Maps
Promiseのリストとマップ
grails.async.PromiseList
and grails.async.PromiseMap
classes respectively.
grails.async.PromiseList
、grails.async.PromiseMap
のクラスとして実装されています。tasks
method of the Promises
class:
Promises
クラスのtasks
メソッドを使用することです。import static grails.async.Promises.*def promiseList = tasks { 2 * 2 }, { 4 * 4}, { 8 * 8 }
assert [4,16,64] == promiseList.get()
tasks
method, when passed a list of closures, returns a PromiseList
. You can also construct a PromiseList
manually:
tasks
メソッドはクロージャのリストを引数に取りPromiseList
を返します。または手動でPromiseList
を構築することもできます。import grails.async.*def list = new PromiseList() list << { 2 * 2 } list << { 4 * 4 } list << { 8 * 8 } list.onComplete { List results -> assert [4,16,64] == results }
The PromiseList
class does not implement the java.util.List interface, but instead returns a java.util.List from the get() method
PromiseList
クラスはjava.util.Listのインタフェースを実装していません。代わりにget()メソッドを使用することでjava.util.Listとして取得できます。
PromiseMap
instances is largely similar. Again you can either use the tasks
method:
PromiseMap
を扱う場合もほとんど同じです。tasks
メソッドを使用する場合は以下のようにします。
import static grails.async.Promises.*def promiseList = tasks one:{ 2 * 2 }, two:{ 4 * 4}, three:{ 8 * 8 }
assert [one:4,two:16,three:64] == promiseList.get()
PromiseMap
manually:
PromiseMap
を使用して構築する場合は以下のようにします。import grails.async.*def list = new PromiseMap() map['one'] = { 2 * 2 } map['two'] = { 4 * 4 } map['three'] = { 8 * 8 } map.onComplete { Map results -> assert [one:4,two:16,three:64] == results }
Promise Factories
Promiseファクトリ
Promises
class uses a grails.async.PromiseFactory
instance to create Promise
instances.
Promises
クラスはPromise
インスタンスを生成するためにgrails.async.PromiseFactory
インスタンスを使用しています。org.grails.async.factory.gpars.GparsPromiseFactory
, however it is possible to swap implementations by setting the Promises.promiseFactory
variable.
org.grails.async.factory.gpars.GparsPromiseFactory
が使用されていますが、Promises.promiseFactory
の値を変更することで実装を差し替えることができます。org.grails.async.factory.SynchronousPromiseFactory
instance that makes it easier to test promises:
org.grails.async.factory.SynchronousPromiseFactory
を提供しており、これを使用することで簡単にpromiseのテストが行えます。import org.grails.async.factory.* import grails.async.*Promises.promiseFactory = new SynchronousPromiseFactory()
PromiseFactory
mechanism is theoritically possible to plug in other concurrency libraries into the Grails framework.
PromiseFactory
の仕組みを使用することで、Grailsに他の並行ライブラリを組み込みということも論理的には可能です。DelegateAsync Transformation
DelegateAsync変換
DelegateAsync
transformation is designed to mitigate this problem by transforming any synchronous API into an asynchronous one.
DelegateAsync
変換は、同期APIを非同期APIに変換することで、この問題を軽減できるように設計しています。class BookService {
List<Book> findBooks(String title) {
// implementation
}
}
findBooks
method executes synchronously in the same thread as the caller. To make an asynchronous version of this API you can define another class as follows:
findBooks
メソッドは呼び出し元と同じスレッドで同期的に実行されます。ここで次のような別クラスを定義することで、このAPIの非同期バージョンを作成できます。import grails.async.*class AsyncBookService { @DelegateAsync BookService bookService }
DelegateAsync
transformation will automatically add a new method that looks like the following to the AsyncBookService
class:
DelegateAsync
変換は、自動的にAsyncBookService
クラスに次のようなメソッドを追加します。Promise<List<Book>> findBooks(String title) {
Promises.task {
bookService.findBooks(title)
}
}
DelegateAsync
変換は非同期に処理を行い、Promiseを返すメソッドを追加します。AsyncBookService
can then be injected into other controllers and services and used as follows:
AsyncBookService
は、他のコントローラやサービスに注入され、次のように使用できます。AsyncBookService asyncBookService def findBooks(String title) { asyncBookService.findBooks(title) .onComplete { List results -> println "Books = ${results}" } }
9.2 非同期GORM
Async Namespace
Asyncネームスペース
async
namespace.
async
ネームスペースを通じて提供されます。import static grails.async.Promises.*def p1 = Person.async.get(1L) def p2 = Person.async.get(2L) def p3 = Person.async.get(3L) def results = waitAll(p1, p2, p3)
async
namespace, all the regular GORM methods are available (even dynamic finders), but instead of executing synchronously, the query is run in the background and a Promise
instance is returned.
async
ネームスペースでは、通常使用可能なすべてのGORMメソッドが使用できます(ダイナミックファインダーのような)。ただし同期的に実行されるのではなく、バックグラウンドでクエリーが実行され、Promise
インスタンスを返します。import static grails.async.Promises.*Person.async.list().onComplete { List results -> println "Got people = ${results}" } def p = Person.async.getAll(1L, 2L, 3L) List results = p.get()
def p1 = Person.async.findByFirstName("Homer") def p2 = Person.async.findByFirstName("Bart") def p3 = Person.async.findByFirstName("Barney") results = waitAll(p1, p2, p3)
Async and the Session
非同期とセッション
def promise = Person.async.findByFirstName("Homer") def person = promise.get() person.firstName = "Bart" person.save()
def promise = Person.async.findByFirstName("Homer") def person = promise.get() person.merge() person.firstName = "Bart"
merge()
is called first because it may refresh the object from the cache or database, which would result in the change being lost. In general it is not recommended to read and write objects in different threads and you should avoid this technique unless absolutely necessary.
merge()
を呼びだしています。これはキャッシュまたはデータベースのデータが再読み込みされ、それまでに行った変更が失われてしまうためです。また一般的に異なるスレッド間でオブジェクトを読み書きすることは推奨されません。もし必要がない限り、この方法は絶対に避けるべきです。LazyInitializationException
errors if you do so. If you plan to access the associated objects of those returned from asynchronous queries you should use eager queries (which is recommended anyway to avoid N+1 problems).
LazyInitializationException
を引き起こすという問題があります。もし非同期クエリから取得したオブジェクトの関連にアクセスする場合は、eagerクエリとして実行してください(N+1問題を避けるために、強くお勧めします)。Multiple Asynchronous GORM calls
複数の非同期GORMの呼び出し
task
method that makes this possible. For example:
task
メソッドを使用することで、より複雑なGORMの非同期処理が可能になります。例えば以下のように使用します。def promise = Person.async.task { withTransaction { def person = findByFirstName("Homer") person.firstName = "Bart" person.save(flush:true) } }Person updatedPerson = promise.get()
task
method differs from the static Promises.task
method in that it deals with binding a new session to the asynchronous thread for you. If you do not use the GORM version and do asynchronous work with GORM then you need to do this manually. Example:
task
メソッドは静的なPromises.task
メソッドとは異なり、非同期処理のスレッドに対して自動的に新いセッションをバインドします。もしGORMのtask
メソッドを使用せず非同期にGORMの処理を行う場合は、これを手動で行う必要があります。import static grails.async.Promises.*def promise = task { Person.withNewSession { // your logic here } }
Async DetachedCriteria
非同期DetachedCriteria
DetachedCriteria
class also supports the async
namespace. For example you can do the following:
async
ネームスペースをサポートしています。例えば次のように使用できます。DetachedCriteria query = Person.where { lastName == "Simpson" }def promise = query.async.list()
9.3 非同期リクエストハンドリング
Promise
mechism discussed previously.
Promise
の仕組みを使い、簡単に非同期レスポンスが作成できるAPIを提供しています。grails.servlet.version = "3.0"
Async Models
非同期モデル
grails.async.PromiseMap
via the Promises.tasks
method:
Promises.tasks
メソッドを使用しgrails.async.PromiseMap
を返すことで非同期にモデルを生成できます。import static grails.async.Promises.* … def index() { tasks books: Book.async.list(), totalBooks: Book.async.count(), otherValue: { // do hard work } }
def index() { def otherValue = … [ books: Book.list() , totalBooks: Book.count(), otherValue: otherValue ] }
PromiseMap
to the model
attribute of the render
method:
render
メソッドのmodel
属性にPromiseMap
を設定します。import static grails.async.Promises.* … def index() { render view:"myView", model: tasks( one:{ 2 * 2 }, two:{ 3 * 3 } ) }
Async Response Rendering
非同期レスポンスのレンダリング
import static grails.async.Promises.* class StockController {def stock(String ticker) { task { ticker = ticker ?: 'GOOG' def url = new URL("http://download.finance.yahoo.com/d/quotes.csv?s=${ticker}&f=nsl1op&e=.csv") Double price = url.text.split(',')[-1] as Double render "ticker: $ticker, price: $price" } } }
Promise
instance from the controller action.
Promise
インスタンスを返します。9.4 Servlet 3.0 Async
Servlet 3.0 Asynchronous Rendering
Servlet 3.0の非同期レンダリング
startAsync
method which returns an instance of the Servlet 3.0 AsyncContext
. Once you have a reference to the AsyncContext
you can use Grails' regular render method to render content:
AsyncContext
インスタンスを返す、startAsync
を使用します。このメソッドを使用してAsyncContext
への参照を取得したら、あとは通常のrenderメソッドを使用してコンテンツをレンダリングするだけです。def index() { def ctx = startAsync() ctx.start { new Book(title:"The Stand").save() render template:"books", model:[books:Book.list()] ctx.complete() } }
complete()
method to terminate the connection.
complete
メソッドを呼び出す必要があることに注意してください。Resuming an Async Request
非同期リクエストの再開
dispatch
method of the AsyncContext
class:
AsyncContext
クラスのdispatch
メソッドを使用します。def index() {
def ctx = startAsync()
ctx.start {
// do working
…
// render view
ctx.dispatch()
}
}