I've developed a promise library for java so let me introduce it.
Overview
- You can easily control asynchronous operations like JavaScript's Promise.
- Supports both synchronous and asynchronous execution.
- All the examples presented in this article are here. https://github.com/riversun/java-promise-examples
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");
}
}
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
UsagePromise.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();
}
}
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();
}
}
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.
Top comments (3)
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.