Synchronizing Java with JavaScript execution in JavaFX WebView

I’m working on a JavaFX app that uses a WebView to display TomTom maps. I need to call a JavaScript method from Java and get the response from a routing function. The issue is that the JavaScript function is asynchronous, and Java doesn’t wait for it to finish.

Here’s what I’ve tried:

public class MapApp extends Application {
    private WebView mapView;
    private JSObject jsWindow;

    @Override
    public void start(Stage stage) {
        mapView = new WebView();
        mapView.getEngine().getLoadWorker().stateProperty().addListener((obs, old, newState) -> {
            if (newState == State.SUCCEEDED) {
                jsWindow = (JSObject) mapView.getEngine().executeScript("window");
                System.out.println("Result: " + jsWindow.call("calculateRoute"));
            }
        });
        // Load map HTML
        mapView.getEngine().load("map.html");
        stage.setScene(new Scene(mapView));
        stage.show();
    }
}

The JavaScript side looks like this:

function calculateRoute() {
    return new Promise((resolve) => {
        setTimeout(() => {
            resolve({ distance: 1000, duration: 600 });
        }, 2000);
    });
}

How can I make Java wait for the JavaScript Promise to resolve before continuing? Any suggestions would be appreciated!

I’ve tackled a similar issue in my JavaFX projects. What worked for me was using a combination of CompletableFuture in Java and a callback mechanism in JavaScript. Here’s the gist:

In Java, create a CompletableFuture and pass a callback function to JavaScript. The JavaScript function should call this callback when the Promise resolves. Then, in Java, you can use CompletableFuture.get() to wait for the result.

On the JavaScript side, modify your function to accept a callback:

function calculateRoute(callback) {
    // Your existing Promise logic
    .then(result => callback(JSON.stringify(result)));
}

In Java, set it up like this:

CompletableFuture<String> future = new CompletableFuture<>();
jsWindow.call('calculateRoute', (JSObject) future::complete);
String result = future.get(); // This will block until the callback is called

This approach has worked reliably for me in production. It maintains the asynchronous nature of JavaScript while allowing Java to wait for the result.

hey zack, try using a callback in js. call promise.then(callback) so when the promise resolves, it sends the result to java. then, in java, handle that callback result to sync with js. hope that helps!

The key is to bridge the asynchronous behavior of JavaScript with a synchronous mechanism in Java. One way to do this is to use a CompletableFuture in Java that waits for the Promise returned by your JavaScript function. The JavaScript function should be modified to directly return the Promise, and you can set up a handler in Java that completes the CompletableFuture when the Promise resolves. This means you can then call get() on the CompletableFuture to block the Java thread until a result is available. This method effectively synchronizes the asynchronous JavaScript event with the Java control flow.