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->{
//异常处理逻辑
})
复制代码