JDeferred は、JQuery の Deferred Object に似た Java Deferred/Promise ライブラリです。
JQuery と Android Deferred Object からインスピレーションを受けています。
JDeferred 1.x を使用している場合は、JDeferred 1.x のドキュメントを参照してください。
.then(…)
.filter(…)
.pipe(…)
.done(…)
.fail(…)
.progress(…)
.always(…)
.pipeAlways(…)
.when(p1, p2, p3, …).then(…)
.race(p1, p2, p3, …).then(…)
.settle(p1, p2, p3, …).then(…)
.when(new Runnable() {…})
.race(new Runnable() {…})
.settle(new Runnable() {…})
Deferred<Integer, Exception, Double> deferred;
deferred.resolve(10);
deferred.reject(new Exception());
deferred.notify(0.80);
< dependency >
< groupId >org.jdeferred.v2</ groupId >
< artifactId >jdeferred-core</ artifactId >
< version >${version}</ version >
</ dependency >
compile 'org.jdeferred.v2:jdeferred-core:${version}'
Maven Central Repository で利用可能なバージョンを見つけてください。
バージョン間の互換性レポート:
Deferred deferred = new DeferredObject ();
Promise promise = deferred . promise ();
promise . done ( new DoneCallback () {
public void onDone ( Object result ) {
...
}
}). fail ( new FailCallback () {
public void onFail ( Object rejection ) {
...
}
}). progress ( new ProgressCallback () {
public void onProgress ( Object progress ) {
...
}
}). always ( new AlwaysCallback () {
public void onAlways ( State state , Object result , Object rejection ) {
...
}
});
遅延オブジェクトへの参照を使用して、アクション/更新をトリガーできます。
deferred . resolve ( "done" );
deferred . reject ( "oops" );
deferred . notify ( "100%" );
2.0.0-Beta2 以降は
.then(...)
の代わりに.filter(...)
使用してください
Deferred d = …;
Promise p = d . promise ();
Promise filtered = p . filter ( new DoneFilter < Integer , Integer >() {
public Integer filterDone ( Integer result )
return result * 10 ;
}
});
filtered . done ( new DoneCallback < Integer >() {
public void onDone ( Integer result ) {
// result would be original * 10
System . out . println ( result );
}
});
d . resolve ( 3 ) -> 30.
2.0.0-Beta2 以降は
.then(...)
の代わりに.pipe(...)
使用してください
Deferred d = ...;
Promise p = d . promise ();
p . pipe ( new DonePipe < Integer , Integer , Exception , Void >() {
public Deferred < Integer , Exception , Void > pipeDone ( Integer result ) {
if ( result < 100 ) {
return new DeferredObject < Integer , Void , Void >(). resolve ( result );
} else {
return new DeferredObject < Integer , Void , Void >(). reject ( new Exception (...));
}
}
}). done (...). fail (...);
d . resolve ( 80 ) -> done !
d . resolve ( 100 ) -> fail !
DeferredManager dm = new DefaultDeferredManager ();
Promise p1 , p2 , p3 ;
// initialize p1, p2, p3
dm . when ( p1 , p2 , p3 )
. done (…)
. fail (…)
必要に応じて Executor サービスを指定することもできます。
DeferredManager dm = new DefaultDeferredManager(myExecutorService);
Callable と Runnable は、追加の作業を行わずに Promise とほぼ同じように使用できます。
DeferredManager dm = new DefaultDeferredManager ();
dm . when ( new Callable < Integer >(){
public Integer call () {
// return something
// or throw a new exception
}
}). done ( new DoneCallback < Integer >() {
public void onDone ( Integer result ) {
...
}
}). fail ( new FailCallback < Throwable >() {
public void onFail ( Throwable e ) {
...
}
});
Callable または Runnable 内の進行状況を通知する必要がある場合は、独自の Deferred オブジェクトと Promise を作成するか、DeferredCallable と DeferredRunnable を使用する必要があります。
独自の Deferred オブジェクトを使用する
final Deferred deferred = ...
Promise promise = deferred . promise ();
promise . then (…);
Runnable r = new Runnable () {
public void run () {
while (…) {
deferred . notify ( myProgress );
}
deferred . resolve ( "done" );
}
}
または、DeferredRunnable を拡張する
DeferredManager dm = …;
dm . when ( new DeferredRunnable < Double >(){
public void run () {
while (…) {
notify ( myProgress );
}
}
}). then (…);
1.0.1以降
通常、このフレームワークを使用する場合は、非同期で処理を行う必要があります。ただし、すべての遅延タスクが完了するまで待つ必要がある場合は、Object.wait メソッドまたは Promise.waitSafely メソッドを使用できます。
Promise p = dm . when (...)
. done (...)
. fail (...)
synchronized ( p )
while ( p . isPending ()) {
try {
p . wait ();
} catch ( InterruptedException e ) { ... }
}
}
あるいは、より簡素化されたショートカットを使用することもできます
Promise p = dm . when (...)
. done (...)
. fail (...)
try {
p . waitSafely ();
} catch ( InterruptedException e ) {
...
}
これを Java 8 Lambda と一緒に使用すると、非常に便利になります。
dm . when (() -> {
return "Hey!" ;
}). done ( r -> System . out . println ( r ));
dm . when (
() -> { return "Hello" ; },
() -> { return "World" ; }
). done ( rs ->
rs . forEach ( r -> System . out . println ( r . getResult ()))
);
複数の引数を指定してwhen
を呼び出すと、シグナルが最初の拒否でfail
か、すべての計算値でシグナルがdone
Promise
が生成されます。
Callable < Integer > c1 = () -> 1 ;
Callable < Integer > c2 = () -> 2 ;
Callable < Integer > c3 = () -> 3 ;
Promise < MultipleResults3 < Integer , Integer , Integer >, OneReject < Throwable >, MasterProgress > p = dm . when ( c1 , c2 , c3 );
p . done ( MultipleResults3 < Integer , Integer , Integer > r -> {
Assert . assertEquals ( r . getFirst (), 1 );
Assert . assertEquals ( r . getSecond (), 2 );
Assert . assertEquals ( r . getThird (), 3 );
});
Callable < Integer > c1 = () -> 1 ;
Callable < Integer > c2 = () -> 2 ;
Callable < Integer > c3 = () -> throw new RuntimeException ( "boom!" );
Promise < MultipleResults3 < Integer , Integer , Integer >, OneReject < Throwable >, MasterProgress > p = dm . when ( c1 , c2 , c3 );
p . done ( MultipleResults3 < Integer , Integer , Integer > r -> Assert . fail ( "should not be called" ))
. fail ( OneReject < Throwable > r -> Assert . assertEquals ( r . getReject (). getMessage (), "boom!" ));
2.0.0以降
複数の引数 (最大 5 つ) を指定してwhen
を呼び出すと、タイプセーフなゲッターを使用して結果が生成されます。
2.0.0以降
複数の引数を指定してrace
を呼び出すと、シグナルが最初の拒否でfail
か、シグナルが最初の解決でdone
Promise
が生成されます。
Callable < Integer > c1 = () -> { Thread . sleep ( 200 ); return 1 ; };
Callable < Integer > c2 = () -> { Thread . sleep ( 100 ); return 2 ; };
Callable < Integer > c3 = () -> { Thread . sleep ( 200 ); return 3 ; };
Promise < OneResult <?>, OneReject < Throwable >, Void > p = dm . race ( c1 , c2 , c3 );
p . done ( OneResult <?> r -> Assert . assertEquals ( r . getResult (), 2 ));
Callable < Integer > c1 = () -> { Thread . sleep ( 200 ); return 1 ; };
Callable < Integer > c2 = () -> { Thread . sleep ( 100 ); throw new RuntimeException ( "boom!" ); };
Callable < Integer > c3 = () -> { Thread . sleep ( 200 ); return 3 ; };
Promise < OneResult <?>, OneReject < Throwable >, Void > p = dm . race ( c1 , c2 , c3 );
p . done ( OneResult <?> r -> Assert . fail ( "should not be called" )
. fail ( OneReject < Throwable > r -> Assert . assertEquals ( r . getReject (). getMessage (), "boom!" ));
2.0.0以降
複数の引数を使用してsettle
を呼び出すと、すべての解決と拒否を収集するPromise
が生成されます。
Callable < Integer > c1 = () -> { Thread . sleep ( 200 ); return 1 ; };
Callable < Integer > c2 = () -> { Thread . sleep ( 100 ); throw new RuntimeException ( "boom!" ); };
Callable < Integer > c3 = () -> { Thread . sleep ( 200 ); return 3 ; };
Promise < AllValues , Throwable , MasterProgress >, Void > p = dm . race ( c1 , c2 , c3 );
p . done ( AllValues r -> {
Assert . assertEquals ( r . get ( 0 ). getValue (), 1 );
Assert . assertTrue ( r . get ( 1 ). getValue () instanceof RuntimeException );
Assert . assertEquals ( r . get ( 2 ). getValue (), 3 );
});
2.0.0以降
場合によっては、タスクが実行中にキャンセルされ、割り当てられているリソースをクリーンアップする必要がある場合があります。 org.jdeferred2.CancellationHandler
インターフェースを実装するタスクを定義することも、そのような実装でDeferredFutureTask
に追加の引数を渡すこともできます。
final DataSource datasource = ...;
class DatabaseTask extends Runnable , CancellationHandler {
@ Override
public void run () {
// perform computation with datasource
}
@ Override
public void onCancel () {
try {
datasource . close ();
} catch ( Exception e ) {
throw new IllegalStateException ( e );
}
}
}
DeferredFutureTask < X > task = new DeferredFutureTask ( new DatabaseTask ());
dm . when ( task ). done (...)
CancellationHandler
追加の引数として渡すこともできます。たとえば、
final DataSource datasource = ...;
class DatabaseTask extends Runnable {
@ Override
public void run () {
// perform computation with datasource
}
}
class DatabaseCancellationHandler implements CancellationHandler {
@ Override
public void onCancel () {
try {
datasource . close ();
} catch ( Exception e ) {
throw new IllegalStateException ( e );
}
}
}
DeferredFutureTask < X > task = new DeferredFutureTask ( new DatabaseTask (), new DatabaseCancellationHandler ());
dm . when ( task ). done (...)
Groovyでも簡単に使えます!
@Grab ( ' org.jdeferred.v2:jdeferred-core:2.0.0 ' )
import org.jdeferred2.*
import org.jdeferred2.impl.*
def deferred = new DeferredObject ()
def promise = deferred . promise()
promise . done { result ->
println " done: $r esult "
} . fail { rejection ->
println " fail: $r ejection "
} . always { state , result , rejection ->
println " always "
}
deferred . resolve( " done " )
1.1.0-Beta1以降
jdeferred-android
が利用可能になり、他の Android ライブラリと同じように含めることができます。また、Android Maven プラグインを使用し、apklib ファイルをビルドします。 Android Maven プラグインを使用する場合は、依存関係を含めることができます。
Maven を使用した APKLIB:
< dependency >
< groupId >org.jdeferred.v2</ groupId >
< artifactId >jdeferred-android</ artifactId >
< version >${version}</ version >
< type >apklib</ type >
</ dependency >
Maven を使用した AAR:
1.2.0-Beta1以降
< dependency >
< groupId >org.jdeferred.v2</ groupId >
< artifactId >jdeferred-android-aar</ artifactId >
< version >${version}</ version >
< type >aar</ type >
</ dependency >
Gradle を使用した AAR:
compile 'org.jdeferred.v2:jdeferred-android-aar:${version}'
// or
compile 'org.jdeferred.v2:jdeferred-android-aar:${version}@aar'
Maven Central Repository で利用可能なバージョンを見つけてください。
jdeferred-android
AndroidDeferredManager
と呼ばれる新しいDeferredManager
実装を導入します。 AndroidDeferredManager
コールバックが UI を更新できるように、バックグラウンド スレッドではなく UI スレッドでコールバックが実行されるようにします。あるいは、コールバックはAndroidExecutionScopeable
インターフェイスを実装して、コールバックを UI スレッドで実行するかバックグラウンド スレッドで実行するかを詳細に制御することもできます。
AndroidDeferredManager
、新しいDeferredAsyncTask
オブジェクトもサポートします。このオブジェクトは Android のAsyncTask
に基づいています。
コールバックを常にバックグラウンド スレッドで実行する必要がある場合は、引き続きDefaultDeferredManager
使用できます。
最後に、JDeferred は SLF4J を使用するため、 slf4j-android
使用してログ メッセージをさらにルーティングできます。
これは、非同期サーブレットで JDeferred を使用する方法のサンプル コードです。
@ WebServlet ( value = "/AsyncServlet" , asyncSupported = true )
public class AsyncServlet extends HttpServlet {
private static final long serialVersionUID = 1L ;
private ExecutorService executorService = Executors . newCachedThreadPool ();
private DeferredManager dm = new DefaultDeferredManager ( executorService );
protected void doGet ( HttpServletRequest request ,
HttpServletResponse response ) throws ServletException , IOException {
final AsyncContext actx = request . startAsync ( request , response );
dm . when ( new Callable < String >() {
@ Override
public String call () throws Exception {
if ( actx . getRequest (). getParameter ( "fail" ) != null ) {
throw new Exception ( "oops!" );
}
Thread . sleep ( 2000 );
return "Hello World!" ;
}
}). then ( new DoneCallback < String >() {
@ Override
public void onDone ( String result ) {
actx . getRequest (). setAttribute ( "message" , result );
actx . dispatch ( "/hello.jsp" );
}
}). fail ( new FailCallback < Throwable >() {
@ Override
public void onFail ( Throwable exception ) {
actx . getRequest (). setAttribute ( "exception" , exception );
actx . dispatch ( "/error.jsp" );
}
});
}
}
DeferredManager.StartPolicy.MANAUL
DeferredManager.StartPolicy.MANUAL
を使用してください。