I have a web page where a javascript calculation in a function takes lot of time to finish and makes the page to freeze. What technique should I use to make sure the javascript does not freeze the browser when the calculation is happening in the background?
-
6include your javascript source code. – Shahrokhian Nov 24 '12 at 22:55
-
4Sounds like a nice use case for web workers. – James M Nov 24 '12 at 22:56
-
1Let's have a look at the existing code. The community can't make suggestions without seeing what you have already. – ajtrichards Nov 24 '12 at 22:56
-
18Read his question. There *is* no code to look at. He's asking for general advice based on an interview question. – Geuis Nov 24 '12 at 22:57
-
this website explains how you could fix the problem a http://blog.rakeshpai.me/2007/10/client-side-performance-optimization-of.html i highly recommend this read. – zero8 Sep 19 '17 at 08:42
-
@Geuis general advice is usually off-topic on SO. It's best to include a [mcve] because specifics about the code and use case do matter, and often play a heavy role in which solution is appropriate for a given situation. Without context, answers tend to devolve into lists of hand-waving advice that may or may not be applicable or appropriate. – ggorlen Apr 11 '22 at 22:50
-
@ggorlen Why did you think responding to a 10 year old (a decade!) comment would be helpful in this context? – Geuis Apr 12 '22 at 07:56
-
1@Geuis because people (such as myself) still visit these threads. It's a common misconception that a question is posted, OP gets their answer, and all parties go home. Rather, future visitors will still curate and use old threads, which are are often closed if off-topic. Conversation is ongoing. Your comment still stands in support of an off-topic post, so it seems fair game to respond to. – ggorlen Apr 12 '22 at 12:58
5 Answers
If you only need to do a calculation and don't need to access the DOM during the long running calculation, then you have two options:
- You can break the calculation up into pieces and do a piece at a time on a
setTimeout()
. On eachsetTimeout()
call, the browser will be free to serve other events and will keep the page alive and responive. When you finish the last piece of the calculation, you can then carry out the result. - You can run the calculation in the background using a webworker in modern browsers. When the calcuation is done in the webworker, it sends a message back to the main thread and you can then update the DOM with the result.
Here's a related answer that also shows an example: Best way to iterate over an array without blocking the UI
-
1Added reference to another related answer that has some examples of using `setTimeout()`. – jfriend00 Nov 24 '12 at 23:06
-
Accepted.Today I learned something new about webworkers and making use of setTimeout(). – Ravi Nov 24 '12 at 23:10
Let me elaborate on @jfriend00's answer by giving a concrete stripped down example. Here is a long-running JavaScript process that can be started by clicking a button. Once it runs, it freezes the browser. The process consists of a long loop that repeats some workload where one iteration takes comparatively little time.
Due to the browser freeze, debugging a script like this is not easy. One alternative to avoid browser freeze is using a web worker. The drawback of that approach is the poor debuggabilty of web workers per se: Tools like Firebug are not supported.
<html>
<head>
<script>
var Process = function(start) {
this.start = start;
}
Process.prototype.run = function(stop) {
// Long-running loop
for (var i = this.start; i < stop; i++) {
// Inside the loop there is some workload which
// is the code that is to be debugged
console.log(i);
}
}
var p = new Process(100);
window.onload = function() {
document.getElementById("start").onclick = function() {
p.run(1000000000);
}
}
</script>
</head>
<body>
<input id="start" type="button" value="Start" />
</body>
</html>
Using a Queue data structure (e.g. http://code.stephenmorley.org/javascript/queues/), an interval timer and some small modification to the control flow of the original process one can build a GUI that doesn't freeze the browser, leaves the process fully debuggable and even allows additional features like stepping, pausing and stopping.
Here is how it goes:
<html>
<head>
<script src="http://code.stephenmorley.org/javascript/queues/Queue.js"></script>
<script>
// The GUI controlling process execution
var Gui = function(start) {
this.timer = null; // timer to check for inputs and/or commands for the process
this.carryOn = false; // used to start/pause/stop process execution
this.cmdQueue = new Queue(); // data structure that holds the commands
this.p = null; // process instance
this.start = start;
this.i = start; // input to the modified process
}
Gui.prototype = {
/**
* Receives a command and initiates the corresponding action
*/
executeCmd: function(cmd) {
switch (cmd.action) {
case "initialize":
this.p = new Process(this);
break;
case "process":
this.p.run(cmd.i);
break;
}
},
/*
* Places next command into the command queue
*/
nextInput: function() {
this.cmdQueue.enqueue({
action: "process",
i: this.i++
});
}
}
// The modified loop-like process
var Process = function(gui) {
this.gui = gui;
}
Process.prototype.run = function(i) {
// The workload from the original process above
console.log(i);
// The loop itself is controlled by the GUI
if (this.gui.carryOn) {
this.gui.nextInput();
}
}
// Event handlers for GUI interaction
window.onload = function() {
var gui = new Gui(100);
document.getElementById("init").onclick = function() {
gui.cmdQueue.enqueue({ // first command will instantiate the process
action: "initialize"
});
// Periodically check the command queue for commands
gui.timer = setInterval(function() {
if (gui.cmdQueue.peek() !== undefined) {
gui.executeCmd(gui.cmdQueue.dequeue());
}
}, 4);
}
document.getElementById("step").onclick = function() {
gui.carryOn = false; // execute just one step
gui.nextInput();
}
document.getElementById("run").onclick = function() {
gui.carryOn = true; // (restart) and execute until further notice
gui.nextInput();
}
document.getElementById("pause").onclick = function() {
gui.carryOn = false; // pause execution
}
document.getElementById("stop").onclick = function() {
gui.carryOn = false; // stop execution and clean up
gui.i = gui.start;
clearInterval(gui.timer)
while (gui.cmdQueue.peek()) {
gui.cmdQueue.dequeue();
}
}
}
</script>
</head>
<body>
<input id="init" type="button" value="Init" />
<input id="step" type="button" value="Step" />
<input id="run" type="button" value="Run" />
<input id="pause" type="button" value="Pause" />
<input id="stop" type="button" value="Stop" />
</body>
</html>
While this approach certainly doesn't fit all long-running scripts one can think of, it certainly can be adapted to any loop-like scenario. I'm using it to port Numenta's HTM/CLA artificial intelligence algorithms to the browser.

- 4,613
- 5
- 27
- 32
Some browsers have only one thread for running your code and updating the UI (in other words, until the calculation is complete, the browser will appear "frozen"). You'll want to try to perform the action asynchronously, in one way or another.
If the calculation is really expensive, you might want to make a call to the server and let the server do the calculation, and callback the client when the calculation is done.
If the calculation is kind of expensive, you can try to do it in chunks on the client. This isn't actually asynchronous (as the client will block while executing each chunk) but the goal is to make the chunks small enough that the blocking is not noticeable.

- 25,722
- 2
- 45
- 57
-
Can you provide a concrete example of a browser that has separate threads for running code and updating the UI? – Gerardo Cauich Jan 25 '19 at 23:41
-
This answer is from 2012, but things are better now; [web workers](https://developer.mozilla.org/en-US/docs/Web/API/Web_Workers_API/Using_web_workers) are now supported by Chrome, Safari, Firefox, IE/Edge, and more. – Jon Newmuis Jan 26 '19 at 07:26
setTimeout(function() { ..code }, 0);
I recommend this for heavy execution time, and also for on load ajax you could try to add
$(window).on("load", function (e) { }); // for jquery v3
if its in the loading process.

- 1,997
- 3
- 15
- 25
-
2We are trying to compute some kind of long-running operations. This only postpones the long-running operation. It will still execute and freezes the browser – canbax Oct 07 '20 at 13:58
I think this should resolve your problem,
function myClickOperation(){
var btn_savebutton2 = document.querySelector("input[id*='savebutton2']");
setTimeout(function () { btn_savebutton2.click() }, 1000);
}
// Full Html content
<html>
<script>
function myClickOperation(){
var btn_savebutton2 = document.querySelector("input[id*='savebutton2']");
document.getElementById('savebutton1').disabled = true;
setTimeout(function () { btn_savebutton2.click() }, 1000);
}
function testClick(){
var idd = document.getElementById("myid");
idd.innerHTML =idd.innerHTML +"<br/>" + new Date();
if(true){
setTimeout(function () { testClick() }, 1);
}
}
</script>
<body>
<input type="button" id="savebutton1" onclick="myClickOperation()" value="Click me" />
<input type="button" id="savebutton2" onclick="testClick()" value="Do not click this" />
<input type="text"/>
<input type="button" value="temp"/>
<div style="height: 300px;overflow-y: scroll;" id="myid"/>
</body>

- 2,842
- 2
- 15
- 11
-
We are trying to compute some kind of long-running operations. This only postpones the long-running operation. It will still execute and freezes the browser – canbax Oct 07 '20 at 13:58
-
Avoid looping statements and use setTimeout it will not freeze. Eg for(1 to 10){}, You can use var x =function(i){ console.log(i);if(i<10) setTimeout(x,1,++i);}, x(1); – George Oct 19 '20 at 02:24
-
"I think this should resolve your problem," -- how, exactly? `if(true){`... why? – ggorlen Apr 11 '22 at 22:53
-
ggorlen, There you can put your own condition... here i have given infinite loop. – George Jun 07 '22 at 14:05