is is possible to wait for an async function to resolve and get its output using graalvm?
why is result containing a bunch of extra stuff?
how do i get the returned value only?
code::
public void main( ) {
try (Context ctx = Context.newBuilder("js")
.allowAllAccess(true)
.option("engine.WarnInterpreterOnly", "false")
.build()) {
String combinedScript = """
// Define the async function
async function dummyAsyncFunction() {
console.log("dummyAsyncFunction()");
// Simulate an asynchronous operation without setTimeout
let start = new Date().getTime();
while (new Date().getTime() - start < 2000) {
// Busy-wait for 1 second
}
// Return the message
return "thanks for waiting";
}
// Call the async function and handle the returned value
dummyAsyncFunction().then(message => {
console.log(message); // Output: thanks for waiting
return message;
}).catch(e => {
console.log("Error: " + e);
});
""";
// Evaluate the combined script
Value result = ctx.eval("js", combinedScript);
System.out.println("result: "+result);
} catch (Exception e) {
}
}
output::
run_server_script()
dummyAsyncFunction()
thanks for waiting
result: Promise{[[PromiseStatus]]: "resolved", [[PromiseValue]]: "thanks for waiting"}
this is the only stackoverflow that i found that was somewhat relevant but it didnt help me::
GraalVM JavaScript in Java - How to identify an async method
GraalVM JavaScript in Java - How to identify an async method
the expected behavior is that result = "thanks for waiting"
UPDATE::
I can do something like this but it looks wrong but it works. it looks wrong because i have to parse the string for the data::
Value result = ctx.eval("js", combinedScript); System.out.println("str: " + result.toString() );
Pattern pattern = Pattern.compile("\[\[PromiseValue\]\]:\\s*\"([^\"]*)\"");
Matcher matcher = pattern.matcher(result.toString());
output::
result: Promise{[[PromiseStatus]]: "resolved", [[PromiseValue]]: "thanks for waiting"}
str: Promise{[[PromiseStatus]]: "resolved", [[PromiseValue]]: "thanks for waiting"} Extracted value: thanks for waiting
then()
The documentation on interopability shows you how to do this. You can call then()
on the promise with a lambda expression.
Value result = ctx.eval("js", combinedScript);
Consumer<Object> finishedHandler = result -> System.out.println("result: " + result);
result.invokeMember("then", finishedHandler);
CountDownLatch
If you want to wait for the Promise
, you could use CountDownLatch
or similar:
Value result = ctx.eval("js", combinedScript);
CountDownLatch awaiter = new CountDownLatch(1);//awaiter.await() will wait until awaiter.countDown() is called
var resultStore = new Object(){
private Object result;//we will store the result here
};
Consumer<Object> finishedHandler = result -> {
resultStore.result = result;
awaiter.countDown();//when the Promise is resolved, the latch is opened
};
result.invokeMember("then", finishedHandler);
awaiter.await();//wait until countDown() is executed i.e. until the promise is resolved
System.out.println("result: " + resultStore.result);
However, note that this would wait forever if the promise never resolves. You could prevent this by interrupting the thread calling await()
(resulting in that method throwing an InterruptedException
) or by calling await
with a timeout.
BlockingQueue
In addition to CountDownLatch
, you could also use a BlockingQueue
where you add the result when the promise is resolved.
Value result = ctx.eval("js", combinedScript);
BlockingQueue queue = new LinkedBlockingQueue();
Consumer<Object> finishedHandler = queue::add;
result.invokeMember("then", finishedHandler);
Object result = queue.take();//waits until the result is added
System.out.println("result: " + result);
As with CountDownLatch
, BlockingQueue
s allow interruption and provide a way to wait with a timeout using the poll
method.
That being said, there is an issue with your JS code: You aren't doing anything asynchronously. If you have an async
method that doesn't use any await
, calling it would just take as long as the code takes to run and it would then return a resolved promise.
I understand that your JS code is just an example but you should never do busy waiting in JS code. If you want to wait in JS, you probably want to do something like this.