java Promise
java promise(GitHub)是Promise A+規範的java實現版本。Promise A+是commonJs規範提出的一種非同步程式設計解決方案,比傳統的解決方案—回撥函式和事件—更合理和更強大。promise實現了Promise A+規範,包裝了java中對多執行緒的操作,提供統一的介面,使得控制非同步操作更加容易。實現過程中參考文件如下:
基本使用:
<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>
複製程式碼
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表示式,更加簡潔。
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)//捕獲中間可能產生的異常
複製程式碼
具體使用 方式參考promise-java非同步程式設計解決方案
Docs
promise規範
promise規範可以參考 Promise A+規範。其中ES6 Promise物件 在Promise A+規範上做了一些補充。java promise在使用上基本與ES6 Promise物件保持一致,部分地方有些許不同,後面會做出說明。 Promise的三個狀態
- pending:等待態,對應執行緒未執行或執行中
- fulfilled:完成態,對應執行緒正常執行完畢,其執行結果稱為終值
- rejected:拒絕態,對應執行緒異常結束,其異常原因稱為拒因
狀態轉移只能由pending->fulfilled或pending->rejected,狀態一旦發生轉移無法再次改變。
Promise
Promise是IPromise的實現,Promise例項一經建立,將立即非同步執行,部分介面如下
IPromise then(OnFulfilledExecutor onFulfilledExecutor)
- 如果當前promise處於pending狀態,阻塞當前執行緒,等待promise狀態轉變為fulfilled或rejected
- 如果處於fulfilled狀態,執行onFulfilledExecutor.onFulfilled(resolvedData)回撥。
- 如果回撥返回一個Promise物件a,以a作為then方法的返回值,如果回撥返回一個普通物件obj,以obj作為終值、狀態為fulfilled包裝一個新Promise作為then方法的返回值
- 如果執行回撥過程中產生異常e,返回一個以e作為拒因、狀態為rejected的新Promise,並拒絕執行接下來的所有Promise直到遇到pCatch。
- 如果處於rejected狀態,執行onRejectedExecutor.onRejected(rejectReason)回撥,返回一個以當前promise的異常作為拒因、狀態為rejected的新Promise,並拒絕執行接下來的所有Promise直到遇到pCatch或pFinally
引數:
IPromise pCatch(OnCatchedExecutor onCatchedExecutor);
then(null,onRejectedExecutor)的別名,但返回不同於then,出現異常時可以選擇不拒絕接下來Promise的執行,可用於異常修正,類似於try{}catch{}
該方法會嘗試捕獲當前promise的異常,最終返回一個新Promise,當被捕獲Promise處於不同的狀態時有不同的行為
- pending:阻塞當前執行緒,等待pending轉變為fulfilled或rejected,行為同then
- fulfilled:不執行回撥,以當前Promise終值和狀態返回一個全新的Promise
- rejected:執行onCatched(Throwable catchReason)回撥。
- 如果onCatched方法返回一個Promise,以這個Promise作為最終返回。
- 如果onCatched方法返回一個非Promise物件obj,以obj作為終值、fulfilled狀態返回一個全新的物件。
- 如果執行回撥過程中產生異常e,以e為拒因、狀態為rejected返回一個新的Promise,並拒絕執行接下來的所有Promise直到再次遇到pCatch
void listen(OnCompleteListener onCompleteListener);
指定一個監聽器,在promise狀態轉為fulfilled或rejected呼叫,該方法不會阻塞執行緒執行,可以多次呼叫指定多個監聽器
void pFinally(OnCompleteListener onCompleteListener);
listen的別名,行為同listen
Status getStatus()
獲取promise的當前狀態
Object getResolvedData()
獲取promise fulfilled狀態下的終值,其餘狀態下時為null
Throwable getRejectedData()
獲取promise rejected狀態下的拒因,其餘狀態下為null
Future getFuture()
獲取promise對應非同步任務的future
boolean cancel()
嘗試取消promise對應的非同步任務,底層呼叫future.cancel(true)。fulfilled或rejected狀態下無效。
Promise.Builder
Promise物件生成器
Builder pool(ExecutorService threadPool)
指定一個執行緒池用於執行promise任務,如果不指定,每一個promise都將啟動一個執行緒
Builder promiseHanler(PromiseHandler promiseExecutor)
指定promise執行器,在promiseHanler的run方法中實現執行緒的具體業務邏輯,注意==promise物件一經建立,將立即執行其中的邏輯==
Builder externalInput(Object externalInput)
向Promise注入一個外部引數,可以在指定PromiseHandler時通過PromiseExecutor.getExternalInput()獲取
int i = 3;
IPromise p = new Promise.Builder()
.externalInput(i).promiseHanler(new PromiseHandler() {
public Object run(PromiseExecutor executor) {
Integer args = (Integer) executor.getExternalInput();
return args*2;
}
}).build();
複製程式碼
Builder promise(IPromise promise)
指定一個promise x,使當前promise接受 x 的狀態
- 如果 x 處於pending, 當前promise 需保持為pending直至 x 轉為fulfilled或rejected
- 如果 x 處於fulfilled,用x的終值值執行當前promise,可以在指定PromiseHandler時通過PromiseExecutor.getPromiseInput()獲取
- 如果 x 處於拒絕態,用相同的據因拒絕當前promise執行
ExecutorService fixedPool = Promise.pool(1);
IPromise promise1 = new Promise.Builder().pool(fixedPool).promiseHanler(executor->3).build();
IPromise promise2 = new Promise.Builder().pool(fixedPool)
.promise(promise1)
.promiseHanler(executor->4+(Integer) executor.getPromiseInput())
.build()
.then(resolvedData->{
System.out.println(resolvedData);
return resolvedData;
}, rejectedReason-> rejectedReason.printStackTrace());
複製程式碼
最終結果返回7,。如果promise1在執行過程中丟擲異常e,promise2將被拒絕執行,將會以e作為拒因,狀態為rejected返回一個新的Promise,最終會執行rejectedReason-> rejectedReason.printStackTrace()
回撥。
IPromise build()
建立一個Promise例項
Promise的靜態方法
static IPromise all(IPromise ...promises)
將多個 Promise 例項p1,...pn,包裝成一個新的 Promise 例項 p,只有當p1-pn的狀態都轉為fulfilled時,p的狀態才為fulfilled,此時p1-pn的返回值包裝為一個陣列Object[r1,...rn]作為p的終值。
只要p1-pn中任意一個被rejected,p的狀態就轉為rejected,將第一個被rejected的promise的拒因作為p的拒因,並嘗試取消其餘promise的執行(內部呼叫future.cancel(true))
static IPromise race(IPromise ...promises)
將多個 Promise p1,...pn例項,包裝成一個新的 Promise 例項 p,只要p1-pn有一個狀態發生改變,p的狀態立即改變。並嘗試取消其餘promise的執行(內部呼叫future.cancel(true))
第一個改變的promise的狀態和資料作為p的狀態和資料
static IPromise resolve()
建立一個終值為null、fulfilled狀態的promise
static IPromise resolve(Object object)
建立一個終值為object、fulfilled狀態的promise
static IPromise resolve(Object object,List args)
將object的then方法以非同步方式執行,then方法的執行結果作為Promise的終值
static IPromise resolve(Object object,String methodName,List args)
將object的指定方法以非同步方式執行,該方法的執行結果作為Promise的終值,目標方法的引數必須按順序包含在List中,如object.doSomething(int a,Map b),用resolve執行為
List args = new ArrayList()
args.add(1);
args.add(map)
Promise.resolve(object,"doSomething",args);
複製程式碼
static IPromise reject(Object reason)
建立一個拒因為reason、rejected狀態的promise
static IPromise pTry(Object object,String methodName,List args)
將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->{
//異常處理邏輯
})
複製程式碼
PromiseHandler
定義非同步邏輯的介面
Object run(PromiseExecutor executor)throws Exception;
run方法中實現具體的業務邏輯,最終run方式是線上程的call方法執行,如果run方法中含有wait、sleep...等鎖操作,可能需要自行處理InterruptedException
。因為該執行緒可能被外部呼叫cancel()或interrupt()方法
PromiseExecutor
promise狀態處理
void resolve(final Object args)
將Promise物件的狀態從“未完成”變為“成功”(即從pending變為fulfilled)。注意該方法一經呼叫,promise狀態將不可改變,如下例,在呼叫executor.resolve(3);後,return之前丟擲一個異常,promise的狀態依舊是fulfilled,終值為3。
new Promise.Builder().promiseHanler(new PromiseHandler(){
@Override
public Object run(PromiseExecutor executor) {
executor.resolve(3);
throw new RuntimeException("error");
return null;
}
}).build()
複製程式碼
在run方法中executor.resolve(3)等同於return 3
@Override
public Object run(PromiseExecutor executor) {
return 3;
}
複製程式碼
大多數情況下建議直接使用return返回promise的終值。
void reject(final Throwable args)
將Promise物件的狀態從“未完成”變為“失敗”(即從pending變為fulfilled)
Object getExternalInput()
獲取通過new Promise.Builder().externalInput(Object externalInput)
方法注入的引數,具體參考Promise.Builder#externalInput(Object externalInput)
Object getPromiseInput()
獲內部promise的執行結果。通過new Promise.Builder().promise(promise1)指定的promise1的執行結果。具體參考
Promise.Builder#promise(IPromise promise)
OnFulfilledExecutor
fulfilled回撥介面
Object onFulfilled(Object resolvedData)throws Exception;
狀態轉為fulfilled時的回撥,返回值可以是IPromise例項或普通物件。如果object是IPromise例項,object作為then方法的返回值,如果object是個普通物件,以object作為終值、狀態為fulfilled包裝一個新Promise作為then方法的返回值
OnRejectedExecutor
rejected回撥介面
void onRejected(Throwable rejectReason)throws Exception;
當Promise轉變為rejected狀態時的回撥
OnCatchedExecutor
rejected回撥介面
Object onCatched(Throwable catchReason)throws Exception;
當發生異常時的回撥,最終返回一個Promise或普通物件,如果是一個普通物件,這個物件將作為下一個Promise的終值
OnCompleteListener
void listen(Object resolvedData,Throwable e);
當Promise執行結束時的回撥(無論是fulfilled還是rejected)
- resolvedData fulfilled狀態時的終值,rejected狀態時為null
- e rejected狀態時的異常資訊,fulfilled狀態時為null
示例
示例1:基本使用
new Promise.Builder().promiseHanler(new PromiseHandler(){
@Override
public Object run(PromiseExecutor executor) {
executor.resolve(3);//返回非同步執行結果3
return null;
}
}).build().then(new OnFulfilledExecutor() {
@Override
public Object onFulfilled(Object resolvedData) {
Integer i = ((Integer)resolvedData)+1;//獲取上一個promsie執行結果3,執行+1
System.out.println(i);//輸出執行結果4
//建立一個新的promise,將4作為該promise的輸入
IPromise p = new Promise.Builder().externalInput(i).promiseHanler(new PromiseHandler() {
@Override
public Object run(PromiseExecutor executor) {
//獲取外部輸入4
Integer args = (Integer) executor.getExternalInput();
executor.resolve(args*2);//執行 4x2
return null;
}
}).build();
return p;//返回該promise p
}
})
.then(new OnFulfilledExecutor() {//執行p的回撥
@Override
public Object onFulfilled(Object args) {
System.out.println(args);//輸出p的執行結果
return args;
}
}, new OnRejectedExecutor() {//捕獲可能出現的異常
@Override
public void onRejected(Throwable rejectedReason) throws Exception {
rejectedReason.printStackTrace();
}
});
複製程式碼
結果
4
8
複製程式碼
示例2
ExecutorService fixedPool = Promise.pool(1);//建立一個執行緒池
//建立promise1
IPromise promise1 = new Promise.Builder().pool(fixedPool).promiseHanler(executor->3).build();
//建立promise2
IPromise promise2 = new Promise.Builder().pool(fixedPool)
.promise(promise1)//讓promise2接受promise1的狀態,優先執行promise1
.promiseHanler(executor->{
//獲取promise1的執行結果,執行promise2的邏輯
return 4+(Integer) executor.getPromiseInput();
})
.build()
.then(resolvedData->{
System.out.println(resolvedData);//列印promise2的執行結果
return resolvedData;
}, rejectedReason-> rejectedReason.printStackTrace());
System.out.println("end");
fixedPool.shutdown();
複製程式碼
結果
7
end
複製程式碼
示例3:錯誤處理
new Promise.Builder().promiseHanler(executor -> 3).build().then(resolvedData->{
System.out.println("a:"+resolvedData);
return new Promise.Builder().promiseHanler(executor -> {
executor.reject(new RuntimeException("err"));//丟擲異常
return null;
}).build();
}).then(resolvedData1 -> {//fulfilled回撥
System.out.println("b:"+resolvedData1);
return resolvedData1;
},rejectReason -> {//rejected回撥
System.err.println("c:"+rejectReason);
});
複製程式碼
結果
a:3
c:java.lang.RuntimeException: err
複製程式碼
示例4:pCatch
new Promise.Builder().promiseHanler(executor -> 0).build()
.then(res0->{
System.out.println("a:"+res0);//輸出 a:0
Thread.sleep(100);
return 1;//返回1
}).then(res1 -> {
throw new RuntimeException("throw error");//丟擲異常
}).then(res2->{
Thread.sleep(100);
System.out.println("b:"+res2);
return 2;
}).pCatch(e->{
Thread.sleep(100);
System.out.println("c:");//輸出c:
e.printStackTrace();
return 3;
}).then(res3->{
Thread.sleep(100);
System.out.println("d:"+res3);//輸出d:3
return 4;
});
複製程式碼
結果
a:0
c:
runtimeException:throw error
d:3
複製程式碼
從上面結果可以看出,在res1出丟擲異常後,拒絕了res2處的執行,被pCatch捕獲,pCatch返回3,被包裝成終值為3、fulfilled狀態的promise,在res3列印d:3。
示例5:Promise.all(IPromise ...promises)
IPromise p1 = new Promise.Builder().promiseHanler(executor -> {
Thread.sleep(1000);
return 1;
}).build();
IPromise p2 = new Promise.Builder().promiseHanler(executor -> {
Thread.sleep(4000);
return 2;
}).build();
IPromise p3 = new Promise.Builder().promiseHanler(executor -> {
Thread.sleep(2000);
return 3;
}).build();
long s = System.currentTimeMillis();
Promise.all(p1,p2,p3).then(resolvedData -> {
Object[] datas = (Object[])resolvedData;
for(Object d:datas){
System.out.println(d);
}
return null;
},e->e.printStackTrace());
System.out.println("耗時:"+(System.currentTimeMillis()-s));
複製程式碼
結果
1
2
3
耗時:4033
複製程式碼
示例6:執行緒取消
Map<String,Boolean> p1Flag = new HashMap<>();
p1Flag.put("flag",true);
IPromise p1 = new Promise.Builder().externalInput(p1Flag).promiseHanler(executor -> {
while (((Map<String,Boolean>)executor.getExternalInput()).get("flag")){
//do something
System.out.println("p1 正在執行任務");
}
System.out.println("p1任務完成,正常結束");
return 1;
}).build();
IPromise p2 = new Promise.Builder().promiseHanler(executor -> {
while (!Thread.currentThread().isInterrupted()){
System.out.println("執行p2正常邏輯");
}
System.err.println("p2執行緒被取消");
return 2;
}).build();
IPromise p3 = new Promise.Builder().promiseHanler(executor -> {
Thread.sleep(10);
throw new RuntimeException("p3丟擲異常");
}).build();
IPromise p4 = new Promise.Builder().finalPromise("4",true).build();
long s = System.currentTimeMillis();
Promise.all(p1,p2,p3,p4).then(resolvedData -> {
Object[] datas = (Object[])resolvedData;
for(Object d:datas){
System.out.println(d);
}
return null;
},e->e.printStackTrace());
System.out.println("耗時:"+(System.currentTimeMillis()-s));
p1Flag.put("flag",false);
複製程式碼
可能的結果如下
p1 正在執行任務
p1 正在執行任務
執行p2正常邏輯
執行p2正常邏輯
p1 正在執行任務
runtimeException:p3丟擲異常
p2執行緒被取消
p1 正在執行任務
p1 正在執行任務
p1 正在執行任務
p1任務完成,正常結束
複製程式碼
從上面結果可以看出,開始p1和p2都在正常執行,當p3丟擲異常後,Promise.all方法立即返回p3的異常並列印,同時取消p1和p2的執行,由於p2判斷了執行緒狀態Thread.currentThread().isInterrupted()
,所以p2執行了正常的退出邏輯。p1仍然在執行,並沒有被取消掉,最後列印p1任務完成,正常結束是因為程式末尾執行了p1Flag.put("flag",false);
,否則p1會永遠迴圈列印。
示例7:同步方法非同步執行
public class ThenTest {
public Integer then(int a,int b){
//列印當前執行現場名稱
System.out.println(Thread.currentThread().getName());
return a+b;
}
public static void main(String[] args){
//列印主執行緒名稱
System.out.println(Thread.currentThread().getName());
List arg = new ArrayList<>();
arg.add(1);
arg.add(2);
//將ThenTest例項then方法非同步執行
Promise.resolve(new ThenTest(),arg).then(resolvedData -> {
System.out.println(resolvedData);
return resolvedData;
}).pCatch(e->{
e.printStackTrace();
return 1;
});
}
}
複製程式碼
結果
main
promise-thread-0
3
複製程式碼