Java-обещание (GitHub) — это версия реализации спецификации Promise A+ для Java. Promise A+ — это решение для асинхронного программирования, предложенное спецификацией commonJs, которое является более разумным и мощным, чем традиционные решения — callback-функции и события. Promise реализует спецификацию Promise A+, оборачивает многопоточные операции в java, предоставляет унифицированный интерфейс и упрощает управление асинхронными операциями. Справочными документами в процессе внедрения являются:
Основное использование: изменить pom.xml
<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>
<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();
В приведенном выше примере создается объект обещания, указывается реализация PromiseHandler, а конкретная бизнес-логика записывается в методе запуска, который аналогичен методу запуска Runable. Как только объект обещания будет создан, он будет немедленно выполнен асинхронно. Рекомендуется использовать лямбда-выражения, которые являются более краткими.
IPromise promise = new Promise.Builder().promiseHanler(executor -> {
return 2*3;
}).build();
Обычно есть два способа получить результат выполнения промиса.then
иlisten
, первое блокирует, а второе не блокирует. Метод then возвращает новый объект обещания, поэтому поддерживается цепочка.
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 возвращает новое обещание. В обратном вызове метода then, если возвращается объект, не являющийся обещанием, обещание считается выполненным обещанием.Если возвращается экземпляр обещания, экземпляр будет выполняться асинхронно.
Если четыре потока 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 wiki
спецификация обещания
Спецификация обещания может относиться кСпецификация обещания A+. вОбъект обещания ES6Внесены некоторые дополнения в спецификацию Promise A+. Обещания Java в основном аналогичны используемым объектам обещаний ES6 с некоторыми небольшими отличиями. Три состояния обещания
- pending: состояние ожидания, соответствующий поток не выполняется или выполняется
- выполнено: завершенное состояние, соответствующий поток выполняется нормально, и вызывается результат его выполненияконечное значение
- отклонено: состояние отклонено, соответствующий поток завершается аварийно, и вызывается причина исключенияотказ
Переход состояния может быть выполнен только путем pending->fulfilled или pending->rejected.После того как переход состояния произойдет, его нельзя будет изменить снова.
использовать
IPromise promise = new Promise.Builder().promiseHandler(handler->2*3).build();//mark1
promise.then(resolvedData -> {
System.out.println(resolvedData);
return null;
});
Создать поток очень просто. Строка, отмеченная mark1, создает промис экземпляра IPromise и указывает асинхронную логику. Вот простая операция умножения. Как только экземпляр промиса будет создан, асинхронная логика будет выполнена немедленно, а результат выполнения или исключение, сгенерированное во время выполнения, будут сохранены в экземпляре промиса. Пул потоков можно указать при создании экземпляра обещания.
ExecutorService pool = Promise.pool(5,10);
IPromise promise = new Promise.Builder().pool(pool).promiseHandler(handler->2*3).build();
Приведенное выше создает пул потоков с минимум 5 и максимум 10. Поток, соответствующий экземпляру обещания, будет выполняться в представленном пуле потоков. Промис может получить результат выполнения с помощью методов then или listen, метод then является блокирующим, а метод listen неблокирующим.
Обычно асинхронной логике требуется доступ к внешним параметрам, а внешние параметры часто не являются окончательными.Промис предоставляет способ ввода внешних параметров во внутреннюю логику.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 изменяет состояние обещания с pending->fulfilled, а метод reject изменяет состояние обещания с pending->rejected. Если обещание уже не находится в ожидании, вызовы разрешения и отклонения не будут иметь никакого эффекта.
new Promise.Builder().promiseHandler(handler->{
int a = 2*3;
handler.resolve(a);
return null;
}).build().listen(((resolvedData, e) -> {
System.out.println(resolvedData);
}));
В приведенном выше примере вычисляется значение a, состояние обещания вручную переводится в состояние выполнено, и значение a используется в качестве окончательного значения обещания. Его также можно вызвать вручнуюhandler.reject(e)
Измените состояние обещания на отклоненное, e(e — экземпляр Throwable) в качестве причины обещания.
В предыдущем и последующих примерах метод handler.resolve(x) не вызывается, но возвращается конкретный результат. Поскольку программа выдает исключение после возврата из метода разрешения, исключение не изменит состояние обещания, исключение будет проглочено внутри, а метод разрешения изменил состояние обещания на выполненное.
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
В приведенном выше примере после ручного вызова метода разрешения, даже если последующая логика выдает исключение, e по-прежнему равно null, потому что состояние обещания было изменено на выполненное, и вся последующая логика (включая возвращаемое значение) не имеет ничего общего с окончательным состоянием обещания. , последующие исключения и возвращаемые результаты будут игнорироваться. Поэтому не рекомендуется напрямую вызывать метод разрешения в не особых случаях, а возвращать результат выполнения напрямую. Этот метод является неявным методом handler.resolve(x), и результат возврата будет использоваться как окончательное значение обещания.
ловушка исключения
Отклоненное состояние обещания соответствует аномальному завершению потока (исключение во время выполнения или вызов executor.reject(e) вручную), который сохраняет экземпляр исключения, и эти исключения поглощаются обещанием и не будут выброшены в поток. Текущая рабочая среда Все попытки... поймать не могут поймать исключение, вызванное внутренней логикой промиса. Promise предоставляет несколько способов обнаружения внутренних исключений:
-
then(onFulfilledExecutor,onRejectedExecutor)
Справочник по API wiki-then -
listen(onCompleteListener),pFinally(onCompleteListener)
Справочник по API wiki-listen -
pCatch(onCatchedExecutor)
Справочник по API wiki-pCatch
Второй параметр метода then является необязательным параметром, в случае возникновения исключения будет выполнен обратный вызов второго параметра.
Поведение listen и pFinally согласовано.Вторым параметром метода listen(Object resolveData, Throwable e) onCompleteListener является объект исключения. Если возникает исключение, e является экземпляром исключения, в противном случае он равен нулю.
pCatch — рекомендуемый метод для перехвата исключений. Then и listen могут только «наблюдать» за исключениями и не могут их исправлять. в конец цепочки вызовов Pass и отклонить выполнение следующего обещания. В отличие от pCatch, после перехвата исключения он может обработать исключение в соответствии с бизнес-логикой и продолжить выполнение следующей цепочки обещаний.
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 обнаруживает исключение, и нормальная логика 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, которое передается следующей обработке промиса и продолжает выполнять цепочку вызовов. pCatch также может напрямую возвращать промис, состояние промиса определяет, продолжать ли выполнение последующей цепочки (Если обещание, возвращаемое pCatch, отклонено, выполнение последующих обещаний все равно будет отклоняться до тех пор, пока не встретится следующее pCatch.)
pCatch может появиться где угодно в цепочке промисов, а количество вхождений не ограничено.Если не возникнет исключения, логика pCatch будет проигнорирована. listen и pFinally могут появляться только в конце цепочки, они будут вызываться независимо от того, возникнет ли исключение (аналогично try...catch...finally).
Комбинация обещаний
Поскольку во время разработки невозможно предсказать, когда завершится выполнение потока, иногда сложно получить результат выполнения потока перед переходом к следующему шагу. Если это одно обещание, вы можете просто использовать метод then, чтобы заблокировать текущий поток и дождаться завершения выполнения потока обещания. Если несколько обещаний выполняются параллельно, вам нужно дождаться выполнения всех обещаний, прежде чем выполнять следующий шаг.Вы можете использовать методы 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.all собирает три промиса в новый промис p. Состояние нового промиса p будет определяться состоянием p1-p3. Если все p1-p3 заканчиваются нормально, состояние p равно выполнено, а его окончательное значение представляет собой массив, содержащий результаты выполнения p1-p3 в порядке поступления. В соответствии со спецификацией обещания, если какой-либо из p1-p3 завершается аварийно или вручную вызывает метод executor.reject() для изменения состояния pn на отклонено, состояние p изменится на отклонено и попытается отменить остальную часть обещания. Для получения подробной информации см.wiki all.
Версия 1.0.1 all также имеет перегруженный метод all(ExecutorService threadPool, final IPromise... promises), который может указывать среду выполнения p и открывать новый поток по умолчанию, если пул потоков не указан.
В некоторых случаях, когда один из p1-p3 ненормальный, вы не хотите, чтобы состояние p немедленно менялось на отклоненное, и пытаетесь отменить выполнение остальных промисов, но вы хотите, чтобы остальные промисы продолжали выполняться. выполнить, вы можете использовать метод waitAll(). Promise.waitAll собирает несколько обещаний в новое обещание p. В отличие от всех, состояние p1-p3 не повлияет на состояние p. Если у самого p нет исключения (waitAll внутренне использует CountDownLatch для обработки нескольких потоков, могут быть исключения), состояние p всегда выполняется, его конечное значение — массив, а значение массива — конечное значение или причина 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 является массивом, и при обходе массива необходимо определить тип значения.
Подобно всем остальным, метод Promise.race оборачивает несколько экземпляров Promise p1,...pn в новый экземпляр Promise p до тех пор, пока состояние p1-pn изменяется (будь то нормальное состояние или ненормальное состояние). состояние p немедленно изменяется и пытается отменить выполнение оставшихся промисов. Состояние и конечное значение первого измененного промиса как состояние и конечное значение p
Promise.resolve и Promise.pTry
Оба этих метода являются статическими методами Promise. Метод Promise.resolve имеет несколько перегрузок, самая важная из нихresolve(Object object,String methodName,List<Object> args)
, этот метод предназначен для асинхронного выполнения указанного метода объекта, а результат выполнения метода используется в качестве конечного значения обещания.Подробнее см.wiki Promise.resolve.
Метод pTry выполняет указанный метод объекта синхронно, а результат выполнения метода используется в качестве конечного значения Promise.Если объект является экземпляром IPromise, параметры methodName и args будут игнорироваться, а экземпляр будет выполняться асинхронно.
Этот метод использует Promise для единообразной обработки синхронных и асинхронных методов.Независимо от того, является ли объект синхронной операцией или асинхронной операцией, вы можете использовать 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->{
//异常处理逻辑
})