3

There has to be an easy way to do this, but I'm new to JS.

I have a javascript program that (1) takes user input, (2) updates the webpage based on that input, then (3) performs a lengthy calculation. The trouble is that the webpage doesn't register the update till after the lengthy calculation. Isn't there a way to pause execution so that the page can update before the long calculation?

I've tried setTimeout and window.setTimeout, but they made no difference.

The program is for playing a game: the user inputs a move, the script updates the position, then calculates its next move. postMessage prints text messages using div.innerHTML; buttonFn takes the input from the user, updates the position, prints a message, then starts the computer calculating.

function buttonFn(arg){
    var hst = histButt;
    hst.push(arg);
    var nwmv = hst.clone();

    postMessage("New move: " + nwmv.join());
    if(status == opposite(comp) && !pauseQ){
    var mvsposs = movesFromPos(posCur,status);
    if(mvsposs.has(nwmv)){
        updatePosCur(nwmv);
        //waitasec();
        if(comp == status && !pauseQ){
        compTurn();
        };
    }
    else{
        histButt = nwmv;
    };
    };
};
  • 1
    A code sample would help here... – deceze Oct 15 '10 at 02:20
  • all process are being processed on client side? – rob waminal Oct 15 '10 at 02:22
  • function postMessage(txt){ messageHist.push(txt); messageHist = messageHist.slice(1); var mesdiv = document.getElementById("messageDiv"); //messageHist.reverse(); mesdiv.innerHTML = messageHist.join("
    "); //messageHist.reverse(); };
    – Andrew Dabrowski Oct 15 '10 at 02:24
  • function buttonFn(arg){ var hst = histButt; hst.push(arg); var nwmv = hst.clone(); postMessage("New move: " + nwmv.join()); if(status == opposite(comp) && !pauseQ){ var mvsposs = movesFromPos(posCur,status); if(mvsposs.has(nwmv)){ updatePosCur(nwmv); //waitasec(); if(comp == status && !pauseQ){ compTurn(); }; } else{ histButt = nwmv; }; }; }; – Andrew Dabrowski Oct 15 '10 at 02:24
  • Yes, everything is done client side. The program is for playing a game: the user inputs a move, the script updates the position, then calculates its next move. postMessage prints text messages using div.innerHTML; buttonFn takes the input from the user, updates the position, prints a message, then starts the computer calculating. – Andrew Dabrowski Oct 15 '10 at 02:25
  • 1
    use a spinner while processing http://stackoverflow.com/questions/51352/how-to-show-a-spinner-while-loading-an-image-via-javascript – zod Oct 15 '10 at 02:28
  • 1
    Put the code in the original question, not the comments – epascarello Oct 15 '10 at 02:29

4 Answers4

6

yes there is, call your function like this. Using setTimeout will allow a page reflow prior to your JS executing.

function buttonFn(arg){
    var hst = histButt;
    hst.push(arg);
    var nwmv = hst.clone();

    postMessage("New move: " + nwmv.join());
    if(status == opposite(comp) && !pauseQ){
    var mvsposs = movesFromPos(posCur,status);
    if(mvsposs.has(nwmv)){
        updatePosCur(nwmv);
        //waitasec();

        if(comp == status && !pauseQ){
        setTimeout(function(){
          compTurn();
        },0);

        };
    }
    else{
        histButt = nwmv;
    };
    };
};

Remember, JS is very event driven friendly. If you can move things off, and call them later do it. Thats the only way we can support multi-threaded like behavior.

setTimeout

Drew
  • 4,683
  • 4
  • 35
  • 50
  • What does the parameter of 0 ms mean? – Andrew Dabrowski Oct 15 '10 at 02:46
  • It tells the JS engine to delay execution. By putting your long running function in a function closure as so, JS will execute the remaining part of buttonFn then execute whatever is inside the setTimeout. If there is a browser reflow (DOM changed), this will occur as well before compTurn runs – Drew Oct 15 '10 at 02:47
  • Thanks! That worked! But why is it necessary to wrap compTurn() in a function construct? And why 0ms rather than say, 100ms? – Andrew Dabrowski Oct 15 '10 at 02:51
  • You can put whatever you want, but I don't think you want compTurn to run later. You just want the application to be responsive. setTimeout executes the function passed to it after a so many milliseconds, in this case 0ms. If you just put setTimeout above your compTurn(), it will execute nothing after 0ms and continue running the rest of your code in procedural fashion. – Drew Oct 15 '10 at 03:00
  • Thanks a lot! I think my mistake was just using setTimeout(compTurn(),100), I didn't realize the expression has to be wrapped. Quoting it seems to work also. – Andrew Dabrowski Oct 15 '10 at 03:06
  • @Andrew `setTimeout(compTurn())` first *executes* `compTurn()`, then passes the result of that function to `setTimeout()`. Use an anonymous function wrap like Drew demonstrates or just *pass* the function like `setTimeout(compTurn)` *without executing it* (no `()`). `setTimeout('compTurn()')` works too, but differently and not in all situations. – deceze Oct 15 '10 at 03:12
  • OK, I think I've got it. Thanks to everyone! – Andrew Dabrowski Oct 15 '10 at 03:16
  • Don't worry, it took me a while to figure out the screwy behavior of setTimeout. It is a pretty crappy function to be honest. – Drew Oct 15 '10 at 03:20
2

If you only need to support modern browsers (or if you use a transpiler), you can now use ES6 features to make this much easier and more in the style the original questioner was trying to do. (I realize the question is 8 years old - no harm in a new, more current answer!)

For example you can do something like this:

// helper function to use a setTimeout as a promise.
function allowUpdate() {
  return new Promise((f) => {
    setTimeout(f, 0);
  });
}

// An infinitely looping operation that doesn't crash the browser.
async function neverStopUpdating(someElement) {
  let i = 0;
  while (true) {
    someElement.innerText = i;
    i++;
    await allowUpdate();
  }
}

If you're trying to do a hard computation you'll want to make sure not to do this await too frequently - in this example, in Chrome at time of writing, i only increments by about 150 per second because the context switch of a setTimeout is not fast (where you'd get hundreds of thousands in a second if you didn't yield for updates). You'd likely want to find a balance, either always perform some number of iterations before allowing an update, or maybe eg. call Date.now() in your loop and yield for an update whenever 100ms have passed since the last time you allowed an update.

Raven Black
  • 349
  • 3
  • 4
0

You can do the update, wait for a bit of time, than do the calculation.

OR

You can use webworkers on browsers that support them.

Without having actual code, that is the best answer that I can give you.

epascarello
  • 204,599
  • 20
  • 195
  • 236
0

JavaScript is single threaded. If you do your calc server side you could get the results via ajax which is called asynchronously, not blocking your ui.

jason saldo
  • 9,804
  • 5
  • 34
  • 41