1

I have a simple tensorflow.js application built with jquery and some basic javascript.

I have button that when clicked, disables itself and toggles a spinner icon to indicate that the model is performing its predictions. When the predictions are finished the default button is restored. However, visually, the UI is not behaving in this way on the browser.

Demo: https://maksimdan.github.io/endpoints/models/pokename.htm

If we substitute predictions with a synchronous sleep function, it behaves as expected.

function sleep(ms) {
    return new Promise(resolve => setTimeout(resolve, ms));
}

$("#raw-gen-button").click(async function(){
    console.log("Generating new predictions...");

    showButtonId("#generating-button");
    await sleep(750);
    // let predictions = await predict_model(5);
    // addGeneratedNamesToUI(predictions);
    showButtonId("#generate-button");

    console.log("Finished generating new predictions...");
    // console.log(predictions);
});
onesiumus
  • 279
  • 6
  • 26
  • 1
    Basically a dupe of https://stackoverflow.com/q/66163875/3702797 The prediction is still made on the main thread. All the methods you are `await`ing for are actually synchronous, so you never let the event-loop loop and the browser can never enter the rendering loop. You could add some `await sleep(0)` in your code to allow it to update the rendering, but it might actually be better to move all the prediction in a dedicated Worker as to not block the UI thread. – Kaiido Feb 17 '21 at 08:42

1 Answers1

-1

The model.predict function is a sync function, which means await keyword will not effect to them, you can remove await keyword when call model.predict, then remove async when defining predict_model function.

The predict_model function is a sync function (like: let a = 1 + 1;), "it" wait until the operation is finished then execute next line of code. In your case, the function will block the main thread (UI thread) while it is executing, this means you can not click, update DOM, run animation... on your browser.

The simple idea is to make predict_model become an async function with setTimeout function, setTimeout will push the "content" to a "queue", and it will not lock your UI thread:

$("#raw-gen-button").click(async function(){
    console.log("Generating new predictions...");

    showButtonId("#generating-button");
    const predictions = await new Promise((resolve) => { // wrap into a Promise to use await keyword
        setTimeout(() => {
            resolve(predict_model(5))
        }, 100);
    });
    addGeneratedNamesToUI(predictions);
    showButtonId("#generate-button");

    console.log("Finished generating new predictions...");
    console.log(predictions);
});
hoangdv
  • 15,138
  • 4
  • 27
  • 48
  • A single setTimeout is not enough to be sure the rendering will happen before the long task starts. You'd at least need rAF + setTimeout, but even then that would not prevent the blocking of the UI thread. For this they need to run the prediction in an other CPU thread. – Kaiido Feb 22 '21 at 03:36