3

So I been stuck on this problem for a while now and have not been able to come up with a solution that works without bugs. Basically I have this code:

public void startExp() {
    root.setCenter(webView);

    //length depends on the exp selected
    for (String expMode : expModes) {
        int r = -1;
        for (int j = 0; j < 2; j++) { // This is the # of the times I would like the same experiment/html page load)
            int temp = r;
            while (r == temp)
                r = ThreadLocalRandom.current().nextInt(0, 2);

            String mode = expMode + ".html";
            System.out.println(mode);
            url = this.getClass().getResource(mode);

            webEngine.getLoadWorker().stateProperty().addListener(
                    (ov, oldState, newState) -> {
                        if (newState == Worker.State.SUCCEEDED) {
                            System.out.println("here");
                            webEngine.executeScript("timer('passage');");
                            // webEngine.executeScript("move('" + textBold + "');");
                            // window.setMember("invoke", new change());
                        }
                    }
            );

            webEngine.load(url.toString());

            // Want this loop to pause here until the JS function finishes executing in webview
        }
    }
}

I want the for loop to somehow "pause" until the javascript function finishes executing and then continue again. The reason I need a for loop and webview for this, is because I need to repeat a given set of experiments (that continuously updates the GUI) that could only (it was also easier) be coded through embedding html pages.

For reference, this is one of the html pages where the JS function takes a string variable from the java function 'startExp()':

<!DOCTYPE html>

<html lang="en">
    <head>
        <title>Hello World</title>
        <link rel="stylesheet" href="./style.css">
    </head>

    <body>
        <p id="demo"></p>
        <script type="application/javascript">
            let i = 0;

            function timer(a) {
                const splitted = a.split(" ");
                const timerID = setInterval(function() {highlight()} , 500);

                function highlight() {
                    if (i == splitted.length) {
                        clearInterval(timerID);
                        invoke.change();
                    }

                    let markS = "<mark>";
                    let markE = "</mark>";
                    let temp =  splitted[i];

                    splitted[i] = markS + splitted[i] + markE;

                    let passage = splitted.join(" ");
                    document.getElementById("demo").innerHTML = passage;
                    splitted[i] = temp;

                    i = i + 1;
                }
            }
        </script>
    </body>
</html>

Also for reference, the variables:

BorderPane root;
static WebView webView ;
static WebEngine webEngine;
static String[] expModes = {"porbTest", "highlightWordsTest"};
String passage = "this is passage 1 and we are seeing if this works. Right now it doesn't but one day it will.";

I tried taking the multithreading approach and using Thread.join() but found out that the webview can only be updated in the Javafx application thread.

Thread.sleep() also doesn't work for me since it sleeps the GUI too (but I need the gui to be updated).

Platform.runlater() defeats the purpose of the for loop since the application goes through the whole for loop and then only runs the last 'mode', this also only works 1% of the time.

I would appreciate if I could get some insight into how I can make this possible .

Thank you

0009laH
  • 1,960
  • 13
  • 27
asad
  • 31
  • 2
  • 2
    Don’t post images of code. Post the code as text, properly formatted. – James_D Jan 29 '23 at 00:16
  • 2
    I did it! I am new to stackoverflow and didn't know the features. – asad Jan 29 '23 at 00:28
  • 2
    Don’t use a loop. Use events. See this example for [calling Java from JavaScript](https://stackoverflow.com/questions/74309273/javafx-webview-call-method-on-returned-java-object/74310505#74310505). Have JavaScript asynchronously do whatever it does and notify your Java app when complete, then your app can instruct the JavaScript to perform the next task in the list if any are left, otherwise you are done. – jewelsea Jan 29 '23 at 00:55
  • @jewelsea thank you for your answer, but can you please clarify what you mean by events? Also, I need to come back to java after the JS function is done executing to execute some other gui events and then go back to the loop to load another html page and execute some other JS function in the new html page...thats why I am using a loop unless there are any other alternatives that are substitutable here. – asad Jan 29 '23 at 01:45
  • This bug could be related: https://bugs.openjdk.org/browse/JDK-8286633 – José Pereda Jan 29 '23 at 10:47
  • By "event" in this context, I just mean some kind of future notification that something occurred (sorry maybe wasn't a good word choice or clearly explained). If you already have the code to "come back to java after the JS function is done executing", then that is really what I referring to. Maintain a list or queue of items to service and each time you receive such a callback, remove the next item from the list or queue and submit it for execution in JavaScript. – jewelsea Jan 29 '23 at 12:45
  • Have your tried to see if [`Selenium`](https://www.selenium.dev/documentation/) could help you? – SedJ601 Jan 29 '23 at 15:30
  • @jewelsea, makes sense, thank you for clarifying. I did try doing that, but it made my program very slow so thats why I scratched that idea. – asad Jan 29 '23 at 19:13
  • @JoséPereda, thanks for that. Looks like it kinda is related to that bug. No wonder I wasn't able to figure out why. I will look into how I could work my way around it. – asad Jan 29 '23 at 19:14
  • @SedJ601, not really sure what that is but I will look into it, thank you! also my question got closed, don't know why. I thought the problem is clear enough – asad Jan 29 '23 at 19:15
  • 1
    @asad, ` I thought the problem is clear enough` - Based on what? Your own viewpoint or the viewpoint of the people who are trying to help you. Create a watered-down full app that demos your current issue. Explain what is happening and what you are trying to achieve. – SedJ601 Jan 30 '23 at 18:29
  • 3
    Note, in many cases, if you have a bunch of actions that occur asynchronously yet must execute sequentially, then using a loop is (nearly?) always wrong in the context of a GUI application. This is where [event-driven programming](https://en.wikipedia.org/wiki/Event-driven_programming) comes into play. GUI applications are the quintessential example of _event-driven_. You don't "wait" for an action to complete, you "react" to an action completing. As noted, you could register some kind of callback that the JavaScript code calls when complete so that the Java code knows to start the next action – Slaw Jan 30 '23 at 21:01
  • 2
    This doesn't involve communication between Java and JavaScript, but [here's an example](https://stackoverflow.com/a/61297536/6395627) of playing the next media after the current one completes without using a loop. – Slaw Jan 30 '23 at 21:02
  • Thank you guys for your suggestions! Finally figured it out and turns out event-Driven programming was the way to go. – asad Feb 11 '23 at 15:38

0 Answers0