0

I want to call a javascript function that takes a long time (5 seconds) to finish without freezing my client's browser. They click the button to start the operation, then they should be notified when it finishes. Currently I have written something like

$(document).ready(function(){
    $('#tokenbutton').click(function(){

        // IMMEDIATE EFFECT
        $('#tokenbutton').attr("disabled",true).val("Computing...");

        var callback = function(resultsFromExpensiveOperation){
            // CALLBACK EFFECTS
            $('#tokenbutton').val("Token computed");
            $('#submitbutton').attr("disabled",false);
            // ...
        };                  

        // FREEZES BROWSER
        doExpensiveOperation(callback);
    });
});

and

doExpensiveOperation = function(callback){
    // ...
    //var results = ...

    callback(results);
};

but this freezes my browser when I run it. How can I change it so this doesn't freeze my browser?

nullUser
  • 1,601
  • 1
  • 12
  • 29
  • What kind of "expensive operation" are we talking about? 5 seconds of processor time on an ordinary modern computer is a LOT of processing. – Pointy Jul 07 '14 at 17:18
  • 1
    Pretty sure you can't do this in a browser since there's only a single JS thread. In the past, I've chunked it up and had a timer fire so I can resume on the next chunk. Ironically, it's a poor man's threading. – David Ehrmann Jul 07 '14 at 17:21
  • https://developer.mozilla.org/en/docs/Web/Guide/Performance/Using_web_workers – Bart Jul 07 '14 at 17:27
  • @Bart Support for web workers seems to be ok, but not amazing. You'll probably miss 15% of browsers. – David Ehrmann Jul 07 '14 at 17:31
  • Asynchronous execution of JavaScript in browsers is only possible with WebWorkers. But as David Ehrmann says, asynchronicity is not necessary to prevent freezing of the browser. http://stackoverflow.com/questions/672732/prevent-long-running-javascript-from-locking-up-browser – Robert Jul 07 '14 at 17:32
  • @DavidEhrmann a web worker is the only way to write a real async function. If you need older browser support, well, you'll have to simulate it. As long as browser support is not a part of the question, I find my comment to be valid... – Bart Jul 07 '14 at 17:38

2 Answers2

1

Use Case

Create a non-Ajax separate process that returns data to a callback.

Solution

Requirements

Browser must:

  • Support HTML5
  • Support Web Workers

Structure

Create a separate file for your separate threaded process with the logic. This file must contain the following code:

postMessage( //data )

Code

if(typeof(Worker) === "function") {
    var worker = new Worker(//uri to js file);
    worker.onmessage = function(event){
        // my call back.
    };
}
Pete
  • 3,246
  • 4
  • 24
  • 43
1

There are no real asynchronous is javascript, thus no multi thread or anything like that. There is a way to make long running function not freeze the browser in certain cases though.

You can use the setInterval or setTimeout function to do small bits of your long running task at a time, thus each bit takes a fraction of a second and then the ui becomes responsive again for a fraction of a second until the next bit runs. This functionally makes the ui stay responsive and does not add much time (if any) onto the processing of the code. For example.

long running code

function doSomething(){
   for(var x = 0; x < 10000; x++){
      // do something 
   }
}
// doing it like this could take forever and lock up the browser

broken up code

var total = 0;
setTimeout(doSomething, 4);

function doSomething(){

     for(total = total; total + 100 < 100; total++){
        // do something 
     }
     if(total < 10000){
        setTimeout(doSomething, 4);
     }
}
// doing it like this stops the browser from freezing but does not save any time.

A few things, I put a time of 4 ms into the setTimout because that is actually the lowest value js will except, even if you put 1 it defaults to 4.

I used a setTimeout pattern instead of a setInterval to prevent the next interval from running before the previous on finishes.

And lastly this pattern does not work for everything. It lends itself best to loop and sequence based operations.

Hope it helps

QBM5
  • 2,778
  • 2
  • 17
  • 24
  • I think you need an additional counter variable in the broken-up version. – Robert Jul 07 '14 at 17:46
  • You may be right, I wrote it off the top of my head, more to show the design pattern than anything else as the OP did give specifics on what his doExpensiveOperation() actually did – QBM5 Jul 07 '14 at 17:58