loading...

How to use Promise in Java

riversun profile image Tom Misawa Updated on ・6 min read

I've developed a promise library for java so let me introduce it.

Overview

Quick Look

Writing a Promise in Javascript

A typical example of using promise in JavaScript is:

Promise.resolve('foo')
    .then(function (data) {
        return new Promise(function (resolve, reject) {
            setTimeout(function () {
                const newData = data + 'bar';
                resolve(newData);
            }, 1);
        });
    })
    .then(function (data) {
        return new Promise(function (resolve, reject) {
            console.log(data);
        });
    });
console.log("Promise in JavaScript");

Writing a Promise in java-promise

Write the same thing using java-promise

import org.riversun.promise.Promise;
public class Example {

    public static void main(String[] args) {
        Promise.resolve("foo")
                .then(new Promise((action, data) -> {
                    new Thread(() -> {
                        String newData = data + "bar";
                        action.resolve(newData);
                    }).start();
                }))
                .then(new Promise((action, data) -> {
                    System.out.println(data);
                    action.resolve();
                }))
                .start();
        System.out.println("Promise in Java");
    }
}

Syntax:

Yes,you can write in a syntax similar to JavaScript as follows:

Promise.resolve()
        .then(new Promise(funcFulfilled1), new Promise(funcRejected1))
        .then(new Promise(functionFulfilled2), new Promise(functionRejected2))
        .start();

Dependency

Maven

<dependency>
    <groupId>org.riversun</groupId>
    <artifactId>java-promise</artifactId>
    <version>1.1.0</version>
</dependency>

Gradle

compile group: 'org.riversun', name: 'java-promise', version: '1.1.0'

Quick Start

Execute sequentially by chained "then"

  • Use Promise.then() to chain operations.
  • Write your logic in Func.run(action,data).
  • Start operation by Promise.start and run asynchronously(run on worker thread)
  • Calling action.resolve makes the promise fullfilled state and passes the result to the next then
public class Example00 {

    public static void main(String[] args) {

        Func function1 = (action, data) -> {
            new Thread(() -> {
                System.out.println("Process-1");
                try {
                    Thread.sleep(500);
                } catch (InterruptedException e) {}
                //Specify result value.(Any type can be specified)
                action.resolve("Result-1");
            }).start();
        };

        Func function2 = (action, data) -> {
            System.out.println("Process-2 result=" + data);
            action.resolve();
        };

        Promise.resolve()
                .then(new Promise(function1))
                .then(new Promise(function2))
                .start();// start Promise operation

        System.out.println("Hello,Promise");
    }
}

Diagram:

Result:

Hello,Promise
Process-1
Process-1 result=Result-1

Tips

It's also OK to just write Promise.then(func).

Promise.resolve()
        .then(function1)
        .then(function2)
        .start();// start Promise operation

Description

What is "Func" ?

Func is a java interface equivalent to JavaScript's Function for argument of #then

public interface Func {
    public void run(Action action, Object data) throws Exception;
}

You can write Func like a JavaScript function.

I want to show two ways of implementing Func class.

No.1)Write Func object in the normal way.

Func function = new Func() {
    @Override
    public void run(Action action, Object data) throws Exception {
        System.out.println("Process");//write your logic
        action.resolve();
    }
};

No.2)Write Func object using lambda expression.

Func function = (action, data) -> {
    System.out.println("Process");//write your logic
    action.resolve();
};

What is "Action" ?

Action object is an argument of Func#run method.

  • Call action.resolve( [fulfillment value] ) to make the Promise's status fulfilled and move on to the next processing(specified by then) with the result(fulfillment value).
action.resolve("Success");
  • Call action.reject( [rejection reason] ) to make the Promise's status rejected and move on to the next processing(specified by then) with the result(rejection reason).
action.reject("Failure");
  • Argument is optional, you can call action.resolve() or action.reject()
action.resolve();//Argument can be omitted

Usage

Rejection

If action.reject() is called, or if an exception thrown while executing Func.run(), rejected status is set to Promise, and the onRejected function specified to then is called.

  • call action.reject
Func function = (action, data) -> {
  action.reject("Failure");
};
  • throw an exception
Func function = (action, data) -> {
  throw new Exception("something");
};

Let's see Promise.then() method,

the 2nd argument of Promise.then() can be set to a Func to receive the result of rejection when receiving the result of then.

  • Syntax

    Usage Promise.then(onFulfilled[, onRejected]);

  • onFulfilled is a Func object called if the Promise is fulfilled.

    You can receive the previous execution "fulfilled" result as an argument named data.

  • onRejected is a Func object called if the Promise is rejected.

    You can receive the previous execution "rejected" result(mainly the objects are exceptions) as an argument named data.

//Rejection
public class ExampleRejection {
    public static void main(String[] args) {
        Promise.resolve()
                .then((action, data) -> {
                    System.out.println("Process-1");
                    action.reject();
                })
                .then(
                        // call when resolved
                        (action, data) -> {
                            System.out.println("Resolved Process-2");
                            action.resolve();
                        },
                        // call when rejected
                        (action, data) -> {
                            System.out.println("Rejected Process-2");
                            action.resolve();
                        })
                .start();// start Promise operation

        System.out.println("Hello,Promise");
    }
}

Diagram:

Result:

Hello,Promise
Process-1
Rejected Process-2

Promise.always

Promise.always() always receive both fulfilled and rejected results.

public class ExampleAlways {

    public static void main(String[] args) {
        Func func2OutReject = (action, data) -> {
            action.reject("I send REJECT");
            //action.resolve("I send RESOLVE");
        };
        Func func2ReceiveAlways = (action, data) -> {
            System.out.println("Received:" + data);
            action.resolve();
        };
        Promise.resolve()
                .then(func2OutReject)
                .always(func2ReceiveAlways)
                .start();
    }
}

Diagram:

Result

Received:I send REJECT

Promise.all

Execute multiple promises at the same time, and after all executions are complete, move to the next processing with then

  • Execute multiple promises simultaneously and wait until all the execution is finished before proceeding.
  • If all finishes with resolve, execution results will be stored as java.util.List in the order of invocation.
  • If there is even one rejection, store that rejection reason in the result when the rejection occurs and move on to the next "then".
import java.util.List;

import org.riversun.promise.Func;
import org.riversun.promise.Promise;

public class ExampleAll {
    public static void main(String[] args) {
        Func func1 = (action, data) -> {
            Promise.sleep(1000);
            System.out.println("func1 running");
            action.resolve("func1-result");
        };
        Func func2 = (action, data) -> {
            Promise.sleep(500);
            System.out.println("func2 running");
            action.resolve("func2-result");
        };
        Func func3 = (action, data) -> {
            Promise.sleep(1500);
            System.out.println("func3 running");
            action.resolve("func3-result");
        };
        Func funcGetResult = (action, data) -> {
            List<Object> resultList = (List<Object>) data;
            for (int i = 0; i < resultList.size(); i++) {
                Object o = resultList.get(i);
                System.out.println("No." + (i + 1) + " result is " + o);
            }
            action.resolve();
        };
        Promise.all(func1, func2, func3)
                .always(funcGetResult)
                .start();
    }
}

Diagram:

Result:

func2 running
func1 running
func3 running
No.1 result is func1-result
No.2 result is func2-result
No.3 result is func3-result

Threading

It is also possible to execute Promise processing on the specified executor.
Note if you use your own executor, remember to shut it down after use.
If you use your own executor, it will NOT be shutdown automatically  

At least one worker thread to be used in Promise.all,
and one thread for overall asynchronous execution, so a total of two or more threads must be needed.

public class Example {

    public static void main(String[] args) {

        final ExecutorService myExecutor = Executors.newFixedThreadPool(5);

        Func func1 = (action, data) -> {
            System.out.println("func1 on " + Thread.currentThread().getName());
            action.resolve();
        };

        Func func2 = (action, data) -> {
            System.out.println("func2 on " + Thread.currentThread().getName());
            action.resolve();
        };

        Func func3 = (action, data) -> {
            System.out.println("func3 on " + Thread.currentThread().getName());
            action.resolve();
        };

        Promise.all(myExecutor, func1, func2, func3)
                .then((action, data) -> {
                    System.out.println("final process on " + Thread.currentThread().getName());
                    myExecutor.shutdown();//If you use your own executor, remember to shut it down after use
                    action.resolve();
                })
                .start();
    }
}

Result:

func1 on pool-1-thread-2
func2 on pool-1-thread-3
func3 on pool-1-thread-4
final process on pool-1-thread-1

SyncPromise

SyncPromise, as the name implies, is a synchronous promise.

While Promise is executed asynchronously, SyncPromise does NOT move next while it is chained by "then".

All other features are the same as Promise.

public class Example02 {

    public static void main(String[] args) {
        Func func1 = (action, data) -> {
            new Thread(() -> {
                System.out.println("Process-1");
                action.resolve();
            }).start();

        };
        Func func2 = (action, data) -> {
            new Thread(() -> {
                System.out.println("Process-2");
                action.resolve();
            }).start();

        };
        SyncPromise.resolve()
                .then(func1)
                .then(func2)
                .start();
        System.out.println("Hello,Promise");
    }
}

Result:

Process-1
Process-2
Hello,Promise

Even if func1 and func2 are executed in a thread,

System.out.println("Hello,Promise") is always executed after that.Because SyncPromise is synchronous execution.

Posted on by:

riversun profile

Tom Misawa

@riversun

UX producer and Full-Stack developer with more than 15 years of experience.

Discussion

pic
Editor guide
 

Why? Use CompletableFuture.

 

Is there a typo in Quick start section's result's output from First example(last line: Result2 should be) ? Or maybe I didn't realize correctly?)

 

Thanks for your comment.
First example's output is as stated.In this example, I wanted to show that the results in the first "then" can be received by the next "then".
Additional questions are welcome.