Подтвердить что ты не робот

Как избежать замораживания браузера при выполнении длительных вычислений в Javascript

У меня есть веб-страница, где вычисление javascript в функции требует много времени для завершения и делает страницу замороженной. Какую технику я должен использовать, чтобы убедиться, что javascript не замораживает браузер, когда вычисление происходит в фоновом режиме?

4b9b3361

Ответ 1

Если вам нужно всего лишь выполнить расчет и не нуждаться в доступе к DOM во время долгого вычисления, тогда у вас есть два варианта:

  • Вы можете разбить вычисление на части и сделать кусок за раз на setTimeout(). При каждом вызове setTimeout() браузер будет иметь возможность обслуживать другие события и будет сохранять страницу вживую и реагировать. Когда вы закончите последнюю часть расчета, вы сможете выполнить результат.
  • Вы можете выполнить расчет в фоновом режиме с помощью веб-мастера в современных браузерах. Когда расчет выполняется в веб-инструменте, он отправляет сообщение обратно в основной поток, и вы можете затем обновить DOM с результатом.

Здесь приведен соответствующий ответ, который также показывает пример: Лучший способ перебора массива без блокировки пользовательского интерфейса

Ответ 2

В некоторых браузерах есть только один поток для запуска вашего кода и обновления пользовательского интерфейса (другими словами, пока расчет не будет завершен, браузер появится "заморожен" ). Вы захотите попытаться выполнить действие асинхронно, так или иначе.

Если вычисление действительно дорогое, вы можете сделать вызов на сервер и позволить серверу выполнять расчет и обратный вызов клиента при выполнении расчета.

Если расчет дорог, вы можете попытаться сделать это в кусках на клиенте. На самом деле это не асинхронно (поскольку клиент блокирует выполнение каждого блока), но цель состоит в том, чтобы сделать куски достаточно маленькими, чтобы блокировка не была заметной.

Ответ 3

Позвольте мне подробнее обсудить @jfriend00, предоставив конкретный урезанный пример. Вот длительный процесс JavaScript, который можно запустить, нажав кнопку. После его запуска он замораживает браузер. Процесс состоит из длинного цикла, который повторяет некоторую рабочую нагрузку, где одна итерация занимает сравнительно мало времени.

Из-за зависания браузера отладка script, как это, непросто. Одной из альтернатив для предотвращения замораживания браузера является использование веб-рабочего. Недостатком такого подхода является низкая степень отлаженности веб-работников как такового: такие инструменты, как Firebug, не поддерживаются.

<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>

Использование структуры данных очереди (например, http://code.stephenmorley.org/javascript/queues/), таймер интервала и небольшая модификация потока управления исходного процесса может создать графический интерфейс, который не блокирует браузер, полностью отлаживает процесс и даже позволяет использовать дополнительные функции например, степпинг, пауза и остановка.

Вот как это делается:

<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>

Хотя этот подход, конечно, не подходит для всех длинных сценариев, о которых можно подумать, это, безусловно, могут быть адаптированы к любому сценарию, подобному циклу. Я использую его для порта Numenta HTM/CLA искусственный интеллектуальных алгоритмов для браузера.

Ответ 4

Я думаю, что это должно решить вашу проблему,

function myClickOperation(){
    var btn_savebutton2 = document.querySelector("input[id*='savebutton2']");
    setTimeout(function () { btn_savebutton2.click() }, 1000);
}

//Полный контент Html

<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>

Ответ 5

 setTimeout(function() { ..code  }, 0);

Я рекомендую это для большого времени выполнения, а также для загрузки ajax вы можете попробовать добавить

$(window).on("load", function (e) { }); // for jquery v3

если он находится в процессе загрузки.