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 javascr
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.
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>
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.
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:
setTimeout()
. On each setTimeout()
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.Here's a related answer that also shows an example: Best way to iterate over an array without blocking the UI
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.