java Promise

Java Promise (GitHub) is the Java implementation version of the Promise A+ specification. Promise A+ is an asynchronous programming solution proposed by the commonJs specification that is more reasonable and powerful than the traditional solution of callback functions and events. Promise implements the Promise A+ specification that wraps multithreaded operations in Java, providing A unified interface that makes it easier to control asynchronous operations. Reference documents in the implementation process are as follows:

  • Promise A + specification
  • ES6 Promise object

Basic use:

<repositories>
    <repository>
      <id>wjj-maven-repo</id>
      <url>https://raw.github.com/zhanyingf15/maven-repo/master</url>
    </repository>
</repositories>
Copy the code
<dependency>
  <groupId>com.wjj</groupId>
  <artifactId>promise</artifactId>
  <version>1.0.0</version>
</dependency>
Copy the code
IPromise promise = new Promise.Builder().promiseHanler(new PromiseHandler() {
    @Override
    public Object run(PromiseExecutor executor) throws Exception {
        return 2*3;
    }
}).build();

Copy the code

The example above creates a Promise object, specifies the Implementation of the PromiseHandler, and writes the specific business logic in the Run method, similar to Runable’s Run method. Once a Promise object is created, it is immediately executed asynchronously. Lambda expressions are recommended for brevity.

IPromise promise = new Promise.Builder().promiseHanler(executor -> {
    return 2*3;
}).build();
Copy the code

Two methods, then (which blocks) and LISTEN (which is non-blocking), are commonly used to obtain the results of a promise’s execution. The then method returns a new Promise object, thus supporting chained calls.

new Promise.Builder().promiseHanler(executor -> {//promise0
    return 2*3;
}).build().then(resolvedData -> {// Return a new promise1
    System.out.println(resolvedData);
    return (Integer)resolvedData+1;
}).then(res2->{
    System.out.println(res2);
    // Create a new promise2 and return
    return new Promise.Builder().externalInput(res2).promiseHanler(executor -> {
        return (Integer)executor.getExternalInput()+2;
    }).build();
}).then(res3->{
    System.out.println(res3);
    return res3;
});
Copy the code

As you can see from above, promise0, PROMISe1, and Promise2 are called in a chain, with each then method returning a new promise. In the callback of the THEN method, if a non-Promise object is returned, then the promise is considered a fulfilled state promise, and if a PROMsie instance is returned, then the instance will be executed asynchronously. If four threads a->b-c-> D need to be executed asynchronously, the call sequence is as follows

Then (dataB->new PromiseC())// then(dataB->new PromiseC())// then(dataC->new PromiseD())// .then(dataD-> XXX)//D callback. PCatch (error-> XXXX)// Catch any possible exceptionCopy the code

See the Promise-Java asynchronous programming solution for details

Docs

Promise specification

The Promise specification can be referenced to the Promise A+ specification. The ES6 Promise object makes some additions to the Promise A+ specification. Java Promises are used in much the same way as ES6 Promise objects, with some minor differences that will be explained later. Three states of Promise

  • Pending: Indicates the pending state. The corresponding thread is not executing or is executing
  • This is a big pity: the completion state, which is the normal completion of the corresponding thread and the execution result is called the final value
  • This state can only be transferred by pending-> depressing or pending-> Rejected. Once the state is transferred, it cannot be changed again.

Promise

Promise is the implementation of IPromise. Once a Promise instance is created, it will be executed asynchronously, with the following interface

IPromise then(OnFulfilledExecutor onFulfilledExecutor)
  • If the current PROMISE is in the pending state, block the current thread and wait for the promise state to change to fulfilled or Rejected
  • If in a fulfilled state, perform onFulfilledExecutor. OnFulfilled (resolvedData) callback.
    • If the callback returns a Promise object A, use a as the return value of the THEN method. If the callback returns a common object obj, wrap a new Promise as the return value of the THEN method with obj as the final value and state as fulfilled
    • If an exception E is raised during the callback, return a new Promise with E as the rejected Promise and reject all subsequent promises until a pCatch is reached.
  • . If in the rejected state, execute onRejectedExecutor onRejected rejectReason callback, abnormal returns a to the current promise as a new promise turned down because of, the status of the rejected, And rejects all subsequent promises until the pCatch or pFinally argument is reached:

IPromise pCatch(OnCatchedExecutor onCatchedExecutor);

Then (null,onRejectedExecutor), but returns different from THEN. If an exception occurs, you can choose not to reject subsequent Promise execution, which can be used for exception correction. Similar to try{}catch{}, this method attempts to catch an exception for the current promise, eventually returning a new promise that behaves differently when in a different state

  • Pending: Blocks the current thread and waits for pending to change to fulfilled or rejected. The behavior is the same as that of THEN
  • This is a big pity: The callback is not performed, and a new Promise will be returned with the current Promise final value and state
  • Rejected: Executes the onCatched(Throwable catchReason) callback.
    • If the onCatched method returns a Promise, the Promise is returned.
    • If the onCatched method returns a non-promise object, obj will be the final value and the fulfilled state will return a brand new object.
    • If exception E is generated during the callback, a new Promise is returned with e as the rejected cause and the status is Rejected, and all subsequent promises are rejected until pCatch is encountered again
void listen(OnCompleteListener onCompleteListener);

This method will not block the thread execution. You can specify multiple listeners multiple times when you call this method

void pFinally(OnCompleteListener onCompleteListener);

An alias for listen, which behaves the same as listen

Status getStatus()

Gets the current state of the Promise

Object getResolvedData()

Gets the final value in the promise depressing state, and null in other states

Throwable getRejectedData()

Gets the reject in the Promise Rejected state and null in the rest states

Future getFuture()

Gets the future of the asynchronous task that the Promise corresponds to

boolean cancel()

Try to cancel the asynchronous task corresponding to the Promise, calling future.cancel(true) underneath. This is a pity or rejected state.

Promise.Builder

Promise object generator

Builder pool(ExecutorService threadPool)

Specify a thread pool to perform the PROMISE task; if not, each promise will start a thread

Builder promiseHanler(PromiseHandler promiseExecutor)

Specify a promise executor that implements the thread’s specific business logic in the run method of the promiseHanler, noting that == the logic in the promise object is executed as soon as it is created

Builder externalInput(Object externalInput)

To Promise to inject an external parameters, can be in specified PromiseHandler through 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();
Copy the code
Builder promise(IPromise promise)

Specify a Promise X so that the current PROMISE accepts the state of X

  • If X is pending, the current promise must remain pending until X moves to fulfilled or Rejected
  • If x is fulfilled with the terminal value of x value to perform the promise, can through PromiseExecutor. On specified PromiseHandler getPromiseInput ()
  • If X is in the reject state, the current promise is rejected with the same data
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());
Copy the code

The final result returns 7,. If promise1 throws an exception (E), promisE2 will be rejected and its status rejected returns a new Promise. Will eventually perform rejectedReason – > rejectedReason. PrintStackTrace () callback.

IPromise build()

Create a Promise instance

A static method for Promise

static IPromise all(IPromise … promises)

Multiple Promise instances P1,… Pn, is packaged into a new Promise instance P. Only when the states of P1-PN are all fulfilled, the state of P will be fulfilled. At this time, the return value of P1-PN is packaged as an array Object[R1,… Rn] as the final value of P. As long as any of the rejected promises in P1-PN is rejected, P’s state will change to Rejected, and the first rejected promise will be rejected as P’s, and the rest promises will be cancelled (call fut.cancel (true) internally).

static IPromise race(IPromise … promises)

Make multiple promises P1,… Pn instance, wrapped as a new Promise instance P, the state of P changes as soon as one of the states of P1-PN changes. And try to cancel the execution of the rest of the promise (internally calling future.cancel(true)). The state and data of the first changed promise are the state and data of P

static IPromise resolve()

Create a PROMISE with a null final value and a fulfilled state

static IPromise resolve(Object object)

This is a pity state. Create a promise with an object and a fulfilled state

static IPromise resolve(Object object,List args)

The then method of an object is executed asynchronously, and the result of the execution of the THEN method is the final value of the Promise

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

The specified method of object is executed asynchronously. The result of the execution of the method is the final value of the Promise. The parameters of the target method must be included in the List in order, such as Object.dosomething (int a,Map b)

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

Create a Promise with the Rejected and Reason states

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

The specified method of Object is executed synchronously, with the result of the execution of the method as the final value of the Promise. If Object is an IPromise instance, the methodName and args arguments are ignored and the instance is executed asynchronously. This method uses Promise to deal with synchronous and asynchronous methods uniformly. Regardless of whether an object is a synchronous operation or an asynchronous operation, then can be used to specify the next process and pCatch method can be used to catch exceptions to avoid the following situations in development

try{ object.doSomething(args1,args2); Then (resolvedData->{// some logic}). Then (resolvedData->{// some logic}). PCatch (e->{// Exception handling logic})}catch(Exception) E){// exception handling logic}Copy the code

Using pTry, you can simplify exception handling

List args = new ArrayList(){args1,args2};
Promise.pTry(object,"doSomething",args)
.then(resolvedData->{
      // Some logic
}).then(resolvedData->{
  // Some logic
}).pCatch(e->{
  // Exception handling logic
})
Copy the code

PromiseHandler

Define an interface for asynchronous logic

Object run(PromiseExecutor executor)throws Exception;

The run method is executed in the call method of the thread. If the run method contains wait, sleep… Such lock operations may require handling InterruptedException yourself. This is because the thread may be called externally to cancel() or interrupt() methods

PromiseExecutor

Promise state processing

void resolve(final Object args)

Change the state of the Promise object from “unfinished” to “successful” (that is, from pending to depressing). Note that once this method is called, the promise state is immutable, as shown in the following example, when executor.resolve(3) is called; Later, an exception is thrown before the return, and the state of the promise is still very depressing, with the final value of 3.

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

Executor.resolve (3) is equivalent to return 3 in the run method

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

In most cases it is recommended to use return directly to return the promise’s final value.

void reject(final Throwable args)

Change the state of the Promise object from “unfinished” to “failed” (that is, from pending to depressing)

Object getExternalInput()

Gets the parameter injected via new promise.Builder ().externalInput(Object externalInput), See promise.builder# externalInput(Object externalInput)

Object getPromiseInput()

Get the execution results of internal promises. The execution result of promise1 specified by new promise.Builder ().promise(promise1). Builder# Promise (IPromise Promise)

OnFulfilledExecutor

The depressing callback interface

Object onFulfilled(Object resolvedData)throws Exception;

The state transitions to a callback that is fulfilled, and the return value can be an IPromise instance or a normal object. If object is an IPromise instance, object will be used as the return value of the THEN method. If object is a common object, a new Promise will be packaged as the return value of the THEN method with object as the final value and state as fulfilled

OnRejectedExecutor

The Rejected callback interface

void onRejected(Throwable rejectReason)throws Exception;

The callback when the Promise changes to the Rejected state

OnCatchedExecutor

The Rejected callback interface

Object onCatched(Throwable catchReason)throws Exception;

A callback when an exception occurs that eventually returns either a Promise or a normal object, which in the case of a normal object will be the final value of the next Promise

OnCompleteListener

void listen(Object resolvedData,Throwable e);

The callback when Promise execution ends (whether this is a pity or Rejected)

  • The final value in the resolvedData depressing state is fulfilled, and the value is null in the Rejected state
  • This is the fulfilled state. The exception information in the rejected state is null

The sample

Example 1: Basic usage
new Promise.Builder().promiseHanler(new PromiseHandler(){
    @Override
    public Object run(PromiseExecutor executor) {
        executor.resolve(3);// Return the asynchronous execution result 3
        return null;
    }
}).build().then(new OnFulfilledExecutor() {
    @Override
    public Object onFulfilled(Object resolvedData) {
        Integer i = ((Integer)resolvedData)+1;// Get the last promsie execution result 3, execute +1
        System.out.println(i);// Output the result 4
        // Create a new promise with 4 as the input for that promise
        IPromise p = new Promise.Builder().externalInput(i).promiseHanler(new PromiseHandler() {
            @Override
            public Object run(PromiseExecutor executor) {
                // Get the external input 4
                Integer args = (Integer) executor.getExternalInput();
                executor.resolve(args*2);/ / execution 4 x2
                return null;
            }
        }).build();
        return p;// Return the promise p
    }
})
.then(new OnFulfilledExecutor() {// execute the p callback
    @Override
    public Object onFulfilled(Object args) {
        System.out.println(args);// Print the execution result of p
        returnargs; }},new OnRejectedExecutor() {// Catch possible exceptions
    @Override
    public void onRejected(Throwable rejectedReason) throws Exception { rejectedReason.printStackTrace(); }});Copy the code

The results of

4
8
Copy the code
Example 2
ExecutorService fixedPool = Promise.pool(1);Create a thread pool
/ / create promise1
IPromise promise1 = new Promise.Builder().pool(fixedPool).promiseHanler(executor->3).build();
/ / create promise2
IPromise promise2 = new Promise.Builder().pool(fixedPool)
    .promise(promise1)// Enable promise1 to accept the status of promise1 and preferentially execute promise1
    .promiseHanler(executor->{
        // Obtain the execution result of promise1 and execute the logic of promise2
        return 4+(Integer) executor.getPromiseInput();
    })
    .build()
    .then(resolvedData->{
        System.out.println(resolvedData);// Prints the execution result of promise2
        return resolvedData;
    }, rejectedReason-> rejectedReason.printStackTrace());
System.out.println("end");
fixedPool.shutdown();
Copy the code

The results of

7
end
Copy the code
Example 3: Error handling
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")); // Throw an exceptionreturnnull; }).build(); }). Then (resolvedData1 -> {// forget system.out.println ();"b:"+resolvedData1);
    returnresolvedData1; },rejectReason -> {//rejected callback system.err. Println ("c:"+rejectReason);
});
Copy the code

The results of

a:3
c:java.lang.RuntimeException: err
Copy the code
Example 4: pCatch
new Promise.Builder().promiseHanler(executor -> 0).build()
  .then(res0->{
    System.out.println("a:"+res0);/ / output a: 0
    Thread.sleep(100);
    return 1;/ / returns 1
}).then(res1 -> {
    throw new RuntimeException("throw error");// Throw an exception
}).then(res2->{
    Thread.sleep(100);
    System.out.println("b:"+res2);
    return 2;
}).pCatch(e->{
    Thread.sleep(100);
    System.out.println("c:");C: / / output
    e.printStackTrace();
    return 3;
}).then(res3->{
    Thread.sleep(100);
    System.out.println("d:"+res3);/ / output d: 3
    return 4;
});
Copy the code

The results of

a:0
c:
runtimeException:throw error
d:3
Copy the code

As can be seen from the above results, after res1 throws an exception, the execution at res2 is rejected and captured by pCatch, which returns 3 and is packaged into a promise with a final value of 3. The depressing state will be fulfilled, and D :3 will be printed in RES3.

Example 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("Time:"+(System.currentTimeMillis()-s));
Copy the code

The results of

1 2 3 Time: 4033Copy the code
Example 6: Thread cancellation
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 is on a mission.");
    }
    System.out.println("P1 task completed. Normal end.");
    return 1;
}).build();
IPromise p2 = new Promise.Builder().promiseHanler(executor -> {
    while(! Thread.currentThread().isInterrupted()){ System.out.println("Perform p2 normal logic");
    }
    System.err.println("P2 thread cancelled");
    return 2;
}).build();
IPromise p3 = new Promise.Builder().promiseHanler(executor -> {
    Thread.sleep(10);
    throw new RuntimeException("P3 throws an exception");
}).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("Time:"+(System.currentTimeMillis()-s));
p1Flag.put("flag".false);
Copy the code

The possible outcomes are as follows

P1 Performing a task P1 Performing a task P2 Normal logic P2 Normal logic P1 performing a task runtimeException: P3 An exception is raised. The P2 thread is canceled. P1 A task is being executedCopy the code

As can be seen from the above results, p1 and P2 are normally executed at the beginning. When P3 throws an exception, the promise. all method immediately returns the exception of P3 and prints it, while canceling the execution of P1 and P2. Since P2 checks the status of thread.currentThread ().isinterrupted (), p2 performs the normal exit logic. P1flag. put(“flag”,false) is executed at the end of the program. Otherwise, P1 will be printed in a loop forever.

Example 7: Synchronous methods execute asynchronously
public class ThenTest {
    public Integer then(int a,int b){
        // Prints the name of the current execution site
        System.out.println(Thread.currentThread().getName());
        return a+b;
    }
    public static void main(String[] args){
        // Prints the main thread name
        System.out.println(Thread.currentThread().getName());
        List arg = new ArrayList<>();
        arg.add(1);
        arg.add(2);
        // Execute the ThenTest instance then method asynchronously
        Promise.resolve(new ThenTest(),arg).then(resolvedData -> {
            System.out.println(resolvedData);
            return resolvedData;
        }).pCatch(e->{
            e.printStackTrace();
            return 1; }); }}Copy the code

The results of

main
promise-thread-0
3
Copy the code