JDeferred เป็นไลบรารี Java Deferred/Promise ที่คล้ายกับ Deferred Object ของ JQuery
แรงบันดาลใจจาก 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%" );
ใช้
.filter(...)
แทน.then(...)
ตั้งแต่ 2.0.0-Beta2
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.
ใช้
.pipe(...)
แทน.then(...)
ตั้งแต่ 2.0.0-Beta2
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 Service ตามความต้องการของคุณได้
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 object และ Promise ของคุณเอง หรือคุณสามารถใช้ DeferredCallable และ DeferredRunnable
ใช้วัตถุที่ถูกเลื่อนออกไปของคุณเอง
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
มีอาร์กิวเมนต์หลายตัวส่งผลให้เกิด Promise
ว่าสัญญาณ fail
ในการปฏิเสธครั้งแรกหรือสัญญาณ done
ด้วยค่าที่คำนวณทั้งหมด
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
การเรียก when
มีอาร์กิวเมนต์หลายตัว (มากถึงห้าตัว) จะสร้างผลลัพธ์ด้วยตัวรับ typesafe
ตั้งแต่ 2.0.0
การเรียกร้องให้ race
โดยมีข้อโต้แย้งหลายข้อส่งผลให้เกิด Promise
ว่าสัญญาณ fail
ในการปฏิเสธครั้งแรกหรือสัญญาณ done
ในการแก้ไขครั้งแรก
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 คุณสามารถรวมการพึ่งพาได้:
APKLIB พร้อม Maven:
< dependency >
< groupId >org.jdeferred.v2</ groupId >
< artifactId >jdeferred-android</ artifactId >
< version >${version}</ version >
< type >apklib</ type >
</ dependency >
AAR กับ Maven:
ตั้งแต่ 1.2.0-Beta1
< dependency >
< groupId >org.jdeferred.v2</ groupId >
< artifactId >jdeferred-android-aar</ artifactId >
< version >${version}</ version >
< type >aar</ type >
</ dependency >
AAR พร้อม Gradle:
compile 'org.jdeferred.v2:jdeferred-android-aar:${version}'
// or
compile 'org.jdeferred.v2:jdeferred-android-aar:${version}@aar'
ค้นหาเวอร์ชันที่มีอยู่บน Maven Central Repository
jdeferred-android
แนะนำการใช้งาน DeferredManager
ใหม่ที่เรียกว่า AndroidDeferredManager
AndroidDeferredManager
ทำให้แน่ใจว่าการโทรกลับจะดำเนินการใน UI Thread แทนที่จะเป็นเธรดพื้นหลังเพื่อให้การโทรกลับทำการอัปเดต UI อีกทางหนึ่ง การโทรกลับยังสามารถใช้อินเทอร์เฟซ AndroidExecutionScopeable
เพื่อควบคุมอย่างละเอียดว่าการโทรกลับควรดำเนินการใน UI Thread หรือเธรดพื้นหลัง
AndroidDeferredManager
ยังรองรับออบเจ็กต์ DeferredAsyncTask
ใหม่อีกด้วย วัตถุนี้อิงตาม AsyncTask
ของ Android
หากคุณต้องการดำเนินการเรียกกลับในเธรดเบื้องหลังเสมอ คุณสามารถใช้ DefaultDeferredManager
ต่อไปได้
สุดท้ายนี้ เนื่องจาก JDeferred ใช้ SLF4J - คุณสามารถกำหนดเส้นทางข้อความบันทึกเพิ่มเติมได้โดยใช้ slf4j-android
นี่คือโค้ดตัวอย่างเกี่ยวกับวิธีใช้ JDeferred กับ Asynchronous Servlet!
@ 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
แทน