Обещания — асинхронное программирование синхронным способом в Java

Java задняя часть открытый источник Promise

java Promise

Java-обещание (GitHub) — это версия реализации спецификации Promise A+ для Java. Promise A+ — это решение для асинхронного программирования, предложенное спецификацией commonJs, которое является более разумным и мощным, чем традиционные решения — callback-функции и события. 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();

В приведенном выше примере создается объект обещания, указывается реализация 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)//捕获中间可能产生的异常

Обратитесь к конкретному использованиюобещание-java решение для асинхронного программирования

Docs

спецификация обещания

Спецификация обещания может относиться кСпецификация обещания A+. вОбъект обещания ES6Внесены некоторые дополнения в спецификацию Promise A+. Промисы Java в основном такие же, как используемые объекты промисов ES6, с некоторыми отличиями в некоторых местах, которые будут объяснены позже. Три состояния обещания

  • pending: состояние ожидания, соответствующий поток не выполняется или выполняется
  • выполнено: завершенное состояние, соответствующий поток выполняется нормально, и вызывается результат его выполненияконечное значение
  • отклонено: состояние отклонено, соответствующий поток завершается аварийно, и вызывается причина исключенияОтказался, потому что
    Государственный переход может быть выполнен только путем ожидания -> выполненного или в ожидании ожидания. После того, как происходит переход состояния, он не может быть изменен снова.

Promise

Promise — это реализация IPromise. После создания экземпляра Promise он будет выполняться асинхронно. Некоторые интерфейсы следующие:

IPromise then(OnFulfilledExecutor onFulfilledExecutor)
  • Если текущее обещание находится в состоянии ожидания, заблокируйте текущий поток и подождите, пока состояние обещания не изменится на выполненное или отклоненное.
  • Если он находится в состоянии выполнения, выполните обратный вызов onFulfilledExecutor.onFulfilled(resolvedData).
    • Если обратный вызов возвращает объект Promise a, используйте a в качестве возвращаемого значения метода then, если обратный вызов возвращает обычный объект obj, используйте obj в качестве конечного значения, а статус выполнен, чтобы обернуть новый Promise в качестве возвращаемого значения тогдашний метод
    • Если во время выполнения обратного вызова возникает исключение e, верните новое обещание с e в качестве причины отклонения и статусом отклонено, и откажитесь от выполнения всех следующих обещаний до тех пор, пока не встретится pCatch.
  • Если он находится в отклоненном состоянии, выполните обратный вызов onRejectedExecutor.onRejected(rejectReason), верните новый промис, за исключением текущего промиса в качестве причины отклонения, и состояние будет отклонено, и откажитесь от выполнения всех следующих промисов, пока pCatch или pFinally встречается
    параметр:

IPromise pCatch(OnCatchedExecutor onCatchedExecutor);

Псевдоним then(null, onRejectedExecutor), но результат отличается от then. Когда возникает исключение, вы можете не отклонять выполнение следующего промиса, который можно использовать для исправления исключения, аналогично try{}catch {}
Этот метод попытается перехватить исключение текущего промиса и, наконец, вернуть новый промис, который ведет себя по-разному, когда захваченный промис находится в другом состоянии.

  • pending: блокирует текущий поток, ожидая, когда ожидание будет выполнено или отклонено, поведение такое же, как и тогда
  • выполнено: не выполнять обратный вызов, вернуть новое обещание с текущим окончательным значением и статусом обещания
  • отклонено: выполнить обратный вызов onCatched(Throwable catchReason).
    • Если наксыпаемый метод возвращает обещание, используйте это обещание как окончательное возвращение.
    • Если метод onCatched возвращает объект obj, не относящийся к Promise, верните совершенно новый объект с obj в качестве конечного значения и выполненного состояния.
    • Если во время выполнения обратного вызова возникает исключение e, возвращается новое обещание с e в качестве причины отклонения, и состояние отклоняется, и все следующие обещания отклоняются до тех пор, пока снова не встретится pCatch.
void listen(OnCompleteListener onCompleteListener);

Укажите прослушиватель, когда состояние обещания изменится на выполненный или отклоненный вызов, этот метод не будет блокировать выполнение потока, вы можете вызывать несколько указанных прослушивателей несколько раз

void pFinally(OnCompleteListener onCompleteListener);

Псевдоним listen, поведение такое же, как listen

Status getStatus()

Получить текущее состояние промиса

Object getResolvedData()

Получить окончательное значение в состоянии выполненного обещания, ноль в других состояниях

Throwable getRejectedData()

Получить причину отклонения в состоянии обещания отклонено, ноль в других состояниях

Future getFuture()

Получить будущее асинхронной задачи, соответствующее обещанию

boolean cancel()

Попробуйте отменить асинхронную задачу, соответствующую обещанию, и вызовите future.cancel(true) внизу. Недействительно в статусе выполнено или отклонено.

Promise.Builder

Генератор объектов обещаний

Builder pool(ExecutorService threadPool)

Укажите пул потоков для выполнения задач обещаний. Если не указано, каждое обещание запускает поток

Builder promiseHanler(PromiseHandler promiseExecutor)

Укажите исполнителя промиса и реализуйте конкретную бизнес-логику потока в методе run promiseHanler.Обратите внимание, что после создания объекта ==promise логика в нем будет выполнена немедленно==

Builder externalInput(Object externalInput)

Внедрить внешний параметр в Promise, который можно получить через PromiseExecutor.getExternalInput(), когда указан PromiseHandler

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)

Укажите обещание x, чтобы текущее обещание приняло состояние x.

  • Если x находится в ожидании, текущее обещание должно оставаться в ожидании до тех пор, пока x не будет выполнено или отклонено.
  • Если x выполнено, выполнить текущее обещание с окончательным значением x, которое можно получить с помощью PromiseExecutor.getPromiseInput(), когда указан PromiseHandler
  • Если x находится в отклоненном состоянии, отклоните текущее обещание по той же причине.
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 в качестве причины отклонения и вернет новое обещание со статусом отклонено, которое в конечном итоге будет выполнено.rejectedReason-> rejectedReason.printStackTrace()Перезвони.

IPromise build()

Создайте экземпляр промиса

Статический метод Promise

static IPromise all(IPromise ...promises)

Упакуйте несколько экземпляров Promise p1,...pn в новый экземпляр Promise p. Только когда статус p1-pn изменится на выполнено, состояние p будет выполнено. В это время возвращаемое значение p1-pn упаковано Для массива Object[r1,...rn] в качестве конечного значения p.
Пока любой из p1-pn отклонен, статус p изменяется на отклонено, отклонение первого отклоненного промиса используется как отклонение p, и предпринимаются попытки отменить выполнение оставшихся промисов (внутренний вызов future. отменить (правда))

static IPromise race(IPromise ...promises)

Упакуйте несколько экземпляров Promise p1,...pn в новый экземпляр Promise p, пока изменяется одно состояние p1-pn, состояние p изменяется немедленно. и попытайтесь отменить выполнение остальной части обещания (внутренние вызовы future.cancel(true))
Состояние и данные первого измененного обещания как состояние и данные p

static IPromise resolve()

Создайте обещание, окончательное значение которого равно нулю и выполнено

static IPromise resolve(Object object)

Создайте обещание с окончательным значением объекта и выполненным состоянием

static IPromise resolve(Object object,List args)

Метод then объекта выполняется асинхронно, а результат выполнения метода then используется как конечное значение Promise.

static IPromise resolve(Object object,String methodName,List args)

Выполнить указанный метод объекта асинхронно.Результат выполнения метода используется как конечное значение Promise.Параметры целевого метода должны быть включены в список по порядку, например, object.doSomething(int а, карта б), которая выполняется с разрешением как

List args = new ArrayList()
args.add(1);
args.add(map)
Promise.resolve(object,"doSomething",args);
static IPromise reject(Object reason)

Создайте обещание, которое отвергает состояние разума и отвергает

static IPromise pTry(Object object,String methodName,List args)

Выполняет указанный метод объекта синхронно.Результат выполнения метода используется в качестве окончательного значения промиса.Если объект является экземпляром 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->{
  //异常处理逻辑
})

PromiseHandler

Интерфейс, определяющий асинхронную логику

Object run(PromiseExecutor executor)throws Exception;

Конкретная бизнес-логика реализована в методе запуска. Окончательный метод запуска выполняется в методе вызова потока. Если метод запуска содержит операции блокировки, такие как ожидание, сон и т. д., его может потребоваться обрабатывать отдельно.InterruptedException. Поскольку поток может вызываться методом отмены() или прерывания() извне.

PromiseExecutor

Обработка статуса обещания

void resolve(final Object args)

Измените состояние объекта Promise с «незавершенного» на «успешное» (т. е. с ожидающего на выполненное). Обратите внимание, что после вызова этого метода состояние обещания не может быть изменено.В следующем примере после вызова executor.resolve(3); перед возвратом создается исключение, состояние обещания по-прежнему выполняется, и конечное значение равно 3.

new Promise.Builder().promiseHanler(new PromiseHandler(){
    @Override
    public Object run(PromiseExecutor executor) {
        executor.resolve(3);
        throw new RuntimeException("error");
        return null;
    }
}).build()

В методе запуска executor.resolve(3) эквивалентен return 3

@Override
public Object run(PromiseExecutor executor) {
    return 3;
}

В большинстве случаев рекомендуется использовать return напрямую, чтобы вернуть окончательное значение промиса.

void reject(final Throwable args)

Измените состояние объекта Promise с «незавершенного» на «сбой» (т. е. с ожидающего на выполненное)

Object getExternalInput()

пройти черезnew Promise.Builder().externalInput(Object externalInput)Параметры, вводимые методом, см. в специальной ссылкеPromise.Builder#externalInput(Object externalInput)

Object getPromiseInput()

Получите результат выполнения внутреннего обещания. Результат выполнения promise1, указанный new Promise.Builder().promise(promise1). конкретная ссылкаPromise.Builder#promise(IPromise promise)

OnFulfilledExecutor

отработанный интерфейс обратного вызова

Object onFulfilled(Object resolvedData)throws Exception;

Обратный вызов, когда статус изменяется на выполнено, возвращаемое значение может быть экземпляром IPromise или обычным объектом. Если объект является экземпляром IPromise, объект используется в качестве возвращаемого значения метода then.Если объект является общим объектом, объект используется в качестве конечного значения, а состояние выполняется для переноса нового промиса в качестве возвращаемого значения. значение тогдашнего метода.

OnRejectedExecutor

отвергнутый интерфейс обратного вызова

void onRejected(Throwable rejectReason)throws Exception;

Обратный вызов, когда обещание переходит в отклоненное состояние

OnCatchedExecutor

отвергнутый интерфейс обратного вызова

Object onCatched(Throwable catchReason)throws Exception;

Когда возникает исключение, обратный вызов в конечном итоге вернет промис или обычный объект.Если это обычный объект, этот объект будет использоваться в качестве конечного значения следующего промиса

OnCompleteListener

void listen(Object resolvedData,Throwable e);

Обратный вызов, когда выполнение промиса заканчивается (будь то выполнено или отклонено)

  • Окончательное значение resolveData при выполнении, 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 и выполненным состоянием, и печатает d: 3 в разрешении3.

Пример 5: Promise.all(IPromise...обещания)
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