java promise(GitHub)是Promise A+規範的java實現版本。Promise A+是commonJs規範提出的一種異步編程解決方案,比傳統的解決方案—回調函數和事件—更合理和更強大。promise實現了Promise A+規範,包裝了java中對多線程的操做,提供統一的接口,使得控制異步操做更加容易。實現過程當中參考文檔以下:java
基本使用:修改pom.xmlgit
<repositories>
<repository>
<id>wjj-maven-repo</id>
<url>https://raw.github.com/zhanyingf15/maven-repo/master</url>
</repository>
</repositories>
複製代碼
<dependency>
<groupId>com.wjj</groupId>
<artifactId>promise</artifactId>
<version>1.0.0</version>
</dependency>
複製代碼
若是maven settings.xml使用了mirror配置,修改mirrorOfes6
<mirror>
<id>nexus</id>
<mirrorOf>*,!wjj-maven-repo</mirrorOf>
<url>http://maven.aliyun.com/nexus/content/groups/public/</url>
</mirror>
複製代碼
IPromise promise = new Promise.Builder().promiseHanler(new PromiseHandler() {
@Override
public Object run(PromiseExecutor executor) throws Exception {
return 2*3;
}
}).build();
複製代碼
上面的例子中建立了一個promise對象,指定PromiseHandler實現,在run方法中寫具體的業務邏輯,相似於Runable的run方法。promise對象一經建立,將當即異步執行。推薦使用lambda表達式,更加簡潔。github
IPromise promise = new Promise.Builder().promiseHanler(executor -> {
return 2*3;
}).build();
複製代碼
獲取promise的執行結果一般使用兩個方法then
和listen
,前者是阻塞的後者是非阻塞的。then方法返回一個新的promise對象,所以支持鏈式調用。編程
new Promise.Builder().promiseHanler(executor -> {//promise0
return 2*3;
}).build().then(resolvedData -> {//返回一個新的promise1
System.out.println(resolvedData);
return (Integer)resolvedData+1;
}).then(res2->{
System.out.println(res2);
//建立一個新的promise2並返回
return new Promise.Builder().externalInput(res2).promiseHanler(executor -> {
return (Integer)executor.getExternalInput()+2;
}).build();
}).then(res3->{
System.out.println(res3);
return res3;
});
複製代碼
從上面能夠看到promise0、promise1和Promise2是鏈式調用的,每一次then方法都返回一個新的promise。在then方法的回調中,若是返回的是一個非promise對象,那麼promise被認爲是一個fulfilled狀態的promise,若是返回的是一個promsie實例,那麼該實例將會異步執行。
假如須要異步順序執行a->b-c->d四個線程,調用順序以下數組
new PromiseA()
.then(dataA->new PromiseB())//A的回調
.then(dataB->new PromiseC())//B的回調
.then(dataC->new PromiseD())//C的回調
.then(dataD->xxx)//D的回調
.pCatch(error->xxxx)//捕獲中間可能產生的異常
複製代碼
docs文檔參考promise wikipromise
promise規範能夠參考 Promise A+規範。其中ES6 Promise對象 在Promise A+規範上作了一些補充。java promise在使用上基本與ES6 Promise對象保持一致,部分地方有些許不一樣。 Promise的三個狀態bash
IPromise promise = new Promise.Builder().promiseHandler(handler->2*3).build();//mark1
promise.then(resolvedData -> {
System.out.println(resolvedData);
return null;
});
複製代碼
建立一個線程很是簡單,mark1標註的行建立一個IPromise實例promise,並指定異步邏輯,這裏簡單地作了個乘法操做。promise實例一經建立,異步邏輯將當即執行,執行結果或執行中拋出的異常將保存在promise實例中。能夠在建立promise實例時指定一個線程池。多線程
ExecutorService pool = Promise.pool(5,10);
IPromise promise = new Promise.Builder().pool(pool).promiseHandler(handler->2*3).build();
複製代碼
上面建立了一個最小爲5最大爲10的線程池,promise實例對應的線程將被提交的線程池中執行。promise能夠經過then或listen方法獲取執行結果,then方法是阻塞的而listen是非阻塞的。
一般狀況下異步邏輯須要訪問外部參數,而外部參數每每並非final的,promise提供了輸入外部參數到內部邏輯的方法externalInput
。異步
Map<String,String> map = ImmutableMap.of("name","張三");
IPromise promise = new Promise.Builder().externalInput(map).promiseHandler(handler->{
Map<String,String> m = (Map<String,String>)handler.getExternalInput();
return "你好:"+m.get("name");
}).build();
複製代碼
resolve和reject是PromiseExecutor類的方法,resolve方法將promise狀態由pending->fulfilled,reject方法將promise狀態由pending->rejected。若是promise已是非pending狀態,resolve和reject調用將無效。
new Promise.Builder().promiseHandler(handler->{
int a = 2*3;
handler.resolve(a);
return null;
}).build().listen(((resolvedData, e) -> {
System.out.println(resolvedData);
}));
複製代碼
上面的例子中,計算出a的值,手動將promise狀態轉移爲fulfilled,並將a的值做爲promise的終值。一樣也能夠手動調用handler.reject(e)
將promise狀態轉爲rejected,e(e爲Throwable實例)做爲promise的據因。
在前面和後面的例子中,並無顯示調用handler.resolve(x)方法,而是return具體的結果。由於在 resolve方法以後return以前程序拋出異常,該異常不會更改promise的狀態,異常會被內部吞掉,resolve方法已經將promise的狀態修改成fulfilled了。
new Promise.Builder().promiseHandler(handler->{
int a = 2*3;
handler.resolve(a);
throw new RuntimeException("err");
}).build()
.listen(((resolvedData, e) -> {
System.out.println(resolvedData);
System.out.println(e==null);
}));
複製代碼
打印結果
6
true
複製代碼
上面的例子中,手動調用resolve方法後,後續邏輯即使是拋出了異常,e仍然是null,由於promise狀態已經轉變爲fulfilled,後續的全部邏輯(包括return的值)已經跟promise的最終狀態無關,後續異常和返回結果將被忽略。所以非特殊狀況下不建議直接調用resolve方法,而是直接return返回執行結果。這種方式是handler.resolve(x)的隱式作法,return的結果將做爲promise的終值
promise的rejected狀態對應線程異常結束(運行時異常或手動調用executor.reject(e)),其據因保存了異常實例,這些異常被promise內部吞掉,並不會拋出到當前運行環境中,全部try...catch是沒法捕獲到promise內部邏輯拋出的異常的。promise提供了多種方式能夠偵測到內部異常:
then(onFulfilledExecutor,onRejectedExecutor)
API 參考wiki-thenlisten(onCompleteListener),pFinally(onCompleteListener)
API 參考wiki-listenpCatch(onCatchedExecutor)
API 參考wiki-pCatchthen方法的第二個參數是可選參數,若是發生異常,第二個參數回調將被執行。
listen和pFinally行爲是一致的,onCompleteListener的listen(Object resolvedData,Throwable e)方法第二參數爲異常對象,若是發生異常,e爲異常實例,不然爲null。
pCatch是推薦使用的異常捕獲方式,then和listen對於異常只能「觀察」不能修正,在promise鏈式調用時,一旦發生異常,then方法只能觀察到異常發生,可是異常仍會向調用鏈後方傳遞,並拒絕後面promise的執行。pCatch不一樣,它捕獲到異常後,能夠自行根據業務邏輯對異常處理,繼續執行後面的promise鏈。
new Promise.Builder().promiseHandler(executor -> 3).build().then(resolvedData->{//p1
System.out.println("a:"+resolvedData);
return new Promise.Builder().promiseHandler(executor -> {//p2
executor.reject(new RuntimeException("err"));
return resolvedData;
}).build();
}).then(resolvedData1 -> {//p3
System.out.println("b:"+resolvedData1);
return "b:"+resolvedData1;
},rejectReason -> {
System.err.println("c:"+rejectReason);
}
).then(resolvedData2 -> {//p4
System.out.println("d:"+resolvedData2);
return "d:"+resolvedData2;
},rejectReason -> {
System.err.println("e:"+rejectReason);
}
);
複製代碼
執行結果
a:3
c:java.lang.RuntimeException: err
e:java.lang.RuntimeException: err
複製代碼
在上面的例子中,p1,p2,p3鏈式調用,p1執行後在p2處手動拋出異常,p2的then偵測到異常,p3,p4的正常邏輯被取消了
new Promise.Builder().promiseHandler(executor -> 3).build().then(resolvedData->{
System.out.println("a:"+resolvedData);
return new Promise.Builder().promiseHandler(executor -> {
executor.reject(new RuntimeException("err"));
return resolvedData;
}).build();
}).pCatch(e->{
System.out.println("捕獲到異常");
return 3;
}).then(resolvedData1 -> {
System.out.println("b:"+resolvedData1);
return "b:"+resolvedData1;
},rejectReason -> {
System.err.println("c:"+rejectReason);
}
).then(resolvedData2 -> {
System.out.println("d:"+resolvedData2);
return "d:"+resolvedData2;
},rejectReason -> {
System.err.println("e:"+rejectReason);
}
);
複製代碼
打印結果
a:3
捕獲到異常
b:3
d:b:3
複製代碼
pCatch捕獲到異常後,返回一個修正值3,這個值會傳遞個下一個promise處理,繼續完成鏈式調用。pCatch也能夠直接返回一個promise,promise的狀態決定是否繼續後續鏈的執行(若是pCatch返回的promise是rejected狀態仍然會拒絕後續promise的執行直到遇到下一個pCatch)
pCatch能夠在promise鏈的任何位置出現,出現的次數不受限制,若是沒有異常出現,將忽略pCatch邏輯。listen和pFinally只能在鏈的末尾出現,不管異常是否發生,它都將被調用(相似於try...catch...finally)。
因爲開發中沒法預計線程何時執行結束,有時須要拿到線程執行結果在進行下一步操做就比較麻煩。若是是單個promise,能夠簡單地使用then方法阻塞當前線程,等待promise線程執行完畢。若是是多個promise並行執行,須要等待全部的promise都執行完畢才能執行下一步,可使用all或者waitAll方法。
IPromise p1 = new Promise.Builder().promiseHandler(executor -> {
Thread.sleep(1000);
return 1;
}).build();
IPromise p2 = new Promise.Builder().promiseHandler(executor -> {
Thread.sleep(4000);
return 2;
}).build();
IPromise p3 = new Promise.Builder().promiseHandler(executor -> {
Thread.sleep(2000);
return 3;
}).build();
Promise.all(p1,p2,p3).then(resolvedData -> {
Object[] datas = (Object[])resolvedData;
for(Object d:datas){
System.out.println(d);
}
return null;
},e->e.printStackTrace());
複製代碼
上面建立了三個promise,Promise.all將三個promise組裝成一個新promise p,新的promise p的狀態將由p1-p3的狀態決定,若是p1-p3所有正常結束,p的狀態是fulfilled,其終值是一個數組,按傳入順序保存p1-p3的執行結果。依據promise規範,若是p1-p3任意一個異常結束或手動調用executor.reject()方法將pn狀態轉爲rejected,p的狀態會轉爲rejected,並嘗試取消其他promise。具體能夠參考wiki all。
1.0.1版本all還有一個重載方法all(ExecutorService threadPool,final IPromise ...promises),能夠指定p的執行環境,不指定線程池默認新開一個線程。
在有些狀況下,當p1-p3的其中一個發生異常時,並不但願p的狀態當即轉變爲rejected並嘗試取消其他promise的執行,而是但願其他promise繼續執行,可使用waitAll()方法。Promise.waitAll將多個promise組裝成一個新promise p,不一樣於all,p1-p3的狀態不會影響p的狀態,若是p自身未發生異常(waitAll內部使用了CountDownLatch處理多個線程,可能會有異常),p的狀態一直是fulfilled,其終值是一個數組,數組值是pn的終值或據因。具體可參考wiki-waitAll,使用方式以下:
IPromise p1 = new Promise.Builder().promiseHandler(handler->2*3).build();
IPromise p2 = new Promise.Builder().promiseHandler(handler->{
throw new RuntimeException("手動拋出異常");
}).build();
IPromise p = Promise.waitAll(p1,p2).then(resolvedData -> {
Object[] datas = (Object[]) resolvedData;
for(Object d:datas){
if(d instanceof Throwable){
((Throwable)d).printStackTrace();
}else{
System.out.println(d);
}
}
return datas;
});
複製代碼
輸出結果
6
java.lang.RuntimeException: 手動拋出異常
複製代碼
p1爲正常執行完畢,其終值爲6,p2手動拋出異常,使用waitAll後,p的終值爲一個數組,遍歷數組須要判斷值的類型。
相似於all,Promise.race方法將多個 Promise p1,...pn實例,包裝成一個新的 Promise 實例 p,只要p1-pn有一個狀態發生改變(不管是轉變爲正常狀態仍是異常狀態),p的狀態當即改變,並嘗試取消其他promise的執行。第一個改變的promise的狀態和終值做爲p的狀態和終值
這兩個方法都是Promise的靜態方法。Promise.resolve方法有多個重載,最重要的一個是resolve(Object object,String methodName,List<Object> args)
,該方法是將object的指定方法以異步方式執行,該方法的執行結果做爲Promise的終值,具體可參考wiki Promise.resolve。
pTry方法將object的指定方法以同步方式執行,該方法的執行結果做爲Promise的終值,若是object爲IPromise實例,將忽略methodName和args參數,異步執行該實例。
該方法是以Promise統一處理同步和異步方法,無論object是同步操做仍是異步操做,均可以使用then指定下一步流程,用pCatch方法捕獲異常,避免開發中出現如下狀況
try{
object.doSomething(args1,args2);//可能會拋出異常
promise.then(resolvedData->{
//一些邏輯
}).then(resolvedData->{
//一些邏輯
}).pCatch(e->{
//異常處理邏輯
})
}catch(Exception e){
//異常處理邏輯
}
複製代碼
使用pTry,能夠簡化異常處理
List args = new ArrayList(){args1,args2};
Promise.pTry(object,"doSomething",args)
.then(resolvedData->{
//一些邏輯
}).then(resolvedData->{
//一些邏輯
}).pCatch(e->{
//異常處理邏輯
})
複製代碼