防止浏览器冻结和崩溃长时间计算

Prevent browser freezing and crashing for long time calculation

本文关键字:崩溃 长时间 计算 冻结 浏览器      更新时间:2023-09-26

我需要检查重复的数据库名称,并更改此名称以避免重复。我使用@ jefr N.建议的脚本

function eliminateDuplicates() {
    var repeats = {};
    var error = false;
    //cache inputs
    var $inputs = $("input[type='text']");
    //loop through inputs and update repeats
    for (i = 0; i < $inputs.length; ++i) {
        //cache current element
        var cur = $inputs[i];
        //remove class
        $(cur).removeClass("double-error");
        //get text of this element
        var text = $(cur).val();
        //no text -- continue
        if (text === "") {
            continue;
            }
        //first time we've came across this value -- intialize it's counter to 1
        if ((text in repeats) === false) {
            repeats[text] = 1;
            }
        //repeat offender. Increment its counter.
        else {
            repeats[text] = repeats[text] + 1;
            }
        //update the the value for this one
        $(cur).val(text + "-" + repeats[text]);
        }
    return error; // always returns false since I'm not sure
                  // when it's supposed to return true.
    }

所以脚本工作良好,但如果我有多达100个条目。但是如果我有几千条记录,浏览器就会冻结。Firefox崩溃了。如何防止浏览器冻结和崩溃,例如添加一些加载线或一些时钟指针?也许我需要使用一些setTimeout()函数或其他东西。请帮助防止这个浏览器冻结和崩溃的问题。

我试过了:

function processLargeArrayAsync(array, fn, maxTimePerChunk, context) {
    context = context || window;
    maxTimePerChunk = maxTimePerChunk || 200;
    var index = 0;
    function now() {
        return new Date().getTime();
    }
    function doChunk() {
        var startTime = now();
        while (index < array.length && (now() - startTime) <= maxTimePerChunk) {
            // callback called with args (value, index, array)
            fn.call(context, array[index], index, array);
            ++index;
        }
        if (index < array.length) {
            // set Timeout for async iteration
            setTimeout(doChunk, 1);
        }
    }    
    doChunk();    
}

processLargeArrayAsync(veryLargeArray, myCallback);

没有成功。Chrome死机,IE11浏览器也死机,Firefox崩溃。什么错了吗?我的记录出现在HTML表中。

有些人建议使用web worker。也许这里有人有这样的实践,有一个有效的例子?

我认为代码中最麻烦的部分是DOM访问:获取输入值并更新它们。

根据webworkers文档,webworkers有其局限性,其中之一就是DOM操作。所以我会放弃这个选项。

为了解决问题,我将这样做:

  1. 改进eliminateDuplicates算法(使其更快)。
  2. 使eliminateDuplicates异步:将元素集划分为较小的元素集,并在不同的事件循环tick (setTimeout)中执行每个计算。

在这里我向你展示一个我想到的解决方案。希望它能给你一些想法,帮助你解决你的问题。

首先,我稍微调整了一下eliminateDuplicates(我称之为modifyDOM)

function modifyDOM(elements, repeats) {
    var input, text, i = 0;
    for (; i < elements.length; i++) {
        input = elements[i];
        text = input.value;
        // Remove class.
        input.className = input.className.replace(/'bdouble-error'b/, '');
        if (text) {
            repeats[text] = ~~repeats[text] + 1;
            input.value = text + "-" + repeats[text];
        }
    }
}

我避免在主循环中使用jQuery,因为它的包装器使事情变慢,在你的情况下,不值得使用它。这些小的改变每10000个元素提高了100ms的性能(差不多)。

我创建了两个使用modifyDOM的函数:一个是异步的,一个是同步的。

function parseElementsNonBlocking(elements, maxChunkSize) {
    var repeats = {},
        nChunks = Math.floor(elements/maxChunkSize),
        i = 0,
        j = 1;
    //loop through inputs and update repeats
    for(; i < nChunks; i++, j++) {
        setTimeout(modifyDOM.bind(null, elements.slice(i, j*maxChunkSize), repeats), 0);
    }
    // Rest
    setTimeout(modifyDOM.bind(null, elements.slice(i), repeats), 0);
}
function parseElementsBlocking(elements) {
    var repeats = {};
    //loop through inputs and update repeats
    modifyDOM(elements, repeats);
}

最后,为了测试所有内容,在DOM准备好时执行一个函数,并创建10,000个输入。然后输出运行上述任何方法所需的时间。

$(function () {
    var inputsDiv = $('#inputs'), i, time;
    for (i = 0; i < 10000; i++) {
        var val = i % 3 === 0 ? 'Mickey' : (i % 3 === 1 ? 'Mouse' : '');
        inputsDiv.append('<input type="text" class="double-error" name="FirstName" value="' + val + '">');
    }
    time = Date.now();
    //parseElementsBlocking($("input[type='text']"));
    parseElementsNonBlocking($("input[type='text']"), 100);
    console.log(Date.now() - time);
});

这是一个使用OODK-JS通过webworkers计算一个包含1,000.000个条目的数组的和的解决方案。

该解决方案使用SynchronizedQueue基础类实现生产者/消费者设计模式:生产者(主线程)为数组的每个块生成一个任务并将其添加到队列中。消费者(webworker)从队列中获取一个任务并执行它,直到没有任务离开。一旦所有任务执行完毕,生成器显示最终结果

// main.js (producer)
          OODK.config({
            'path': {
              'oodk': '../src',
              'workspace': 'workspace'
            }
          });
          OODK(function($, _){
            $.import('{oodk}/foundation/utility/Thread', '[util.concurrent]', '{workspace}/project/Task');
            // array helper class to handle arrays
            var ArrayHelper = $.class(function($, µ, _){
              $.static(function($, µ, _){
                // slice an array into chunks using chunkLength argument
                // as delimiter 
                $.public(function slice(arr, chunkLength){
                  return arr.reduce(function(arr, val, index){
                    var chunkIndex = Math.floor(index/chunkLength); 
                    if(!arr[chunkIndex]) {
                      arr[chunkIndex] = [];
                    }
                    arr[chunkIndex].push(val);
                    return arr;
                  }, []);
                });
                // generate an array of len argument length
                // containing random values 
                $.public(function random(len){
                  var arr = [];
                  for(var i =0; i<len; i++){
                    arr.push(Math.random()*10);
                  }
                  return arr;
                })
              });
            });
            // class to handle a pool of thread
            var ThreadPool = $.class(function($, µ, _){
              // number of threads to instantiate
              $.private('num');
              // queue to works with
              $.private('queue');
              $.public(function __initialize(num, queue){
                _.num = num;
                _.queue = queue;
              });
              // start the pool
              $.public(function start(){
                // bind listeners
                var threadListener= $.new(Producer);
                for(var i=0; i<_.num; i++){
                  // instantiate consumers
                  var consumer = $.new(OODK.foundation.util.Thread, "consumer.js");
                  $.on(consumer, 'thread.ready', threadListener);
                  consumer.start();
                }
                $.on(_.queue, 'synchronizedQueue.taskDone', threadListener);
              });
            });
            // Event Listener for the thread
            var Producer = $.implements(OODK.foundation.EventListener).class(function($, µ, _){
              // number of task done
              $.private('taskDone', 0);
              // final result
              $.private('finalResult', 0);
              $.private(function __processEvent(evt){
                if(evt.getType() === 'thread.ready'){
                  // the thread is ready, synchronize the queue with the current thread
                  queue.synchronize(evt.getTarget());
                }else if(evt.getType() == 'synchronizedQueue.taskDone'){
                  //message received from the consumer that it has performed a task
                  _.taskDone++;
                  var cqueue = evt.getTarget();
                  var chunkResult = evt.getData();
                  _.finalResult += chunkResult;
                  jQuery('#chunksDone').text(_.taskDone);
                  if(cqueue.getCapacity() == _.taskDone){
                    // once all tasks are performed display the final result
                    $.log('final sum is ' + _.finalResult);
                  }else{
                    // each time a chunk is calculated display the intermediate result 
                    $.log('intermediate result ' + _.finalResult);
                  }
                }
              });
            });
            // generate a large array of 1.000.000 random values
            var myHugeArray = ArrayHelper.self.random(1000000);
            // split this array into chunks of 2500 length
            var chunks = ArrayHelper.self.slice(myHugeArray, 25000);
            // instantiate a synchronized queue setted as size the number of chunks
            var queue = $.new(OODK.foundation.util.concurrent.SynchronizedQueue, chunks.length);
            // for each chunk create a task and add it to queue
            for(var i=0; i<chunks.length; i++){
              var chunk = chunks[i];
              // create a task for each chunk of the array
              var task = OODK.project.Task.self.factory(chunk);
              // and add it to the queue
              queue.put(task);
            }
            // instantiate a pool of 2 threads working on the given queue
            var threadPool = $.new(ThreadPool, 2, queue);
            // start the pool
            threadPool.start();
            $.log('calculate the sum of an array of 1.000.000 entries using 2 threads ...');
          });

消费者(网络工作者)

//consumer.js
OODK.config({
  'path': {
    'oodk': '../src',
    'workspace': 'workspace'
  }
});
OODK(function($, _){
  // import the concurrent API package as well as the task class
  $.import('[util.concurrent]', '{workspace}/project/Task');
  // start the synchronizer
  OODK.foundation.util.concurrent.SynchronizedObject.self.start();
  // EventListener Class to handle synchronized queue events
  $.implements(OODK.foundation.EventListener).class(function Consumer($, µ, _){
    $.protected(function __processEvent(evt){
      if(evt.getType() == 'synchronizedQueue.ready'){
        //queue is synchronized
        var queue = evt.getTarget();
        // bind listener
        $.on(queue, 'synchronizedQueue.elementRetrieved', this);
        // take a task: get the heap of the stack and delete it
        queue.take();
      }else if(evt.getType() == 'synchronizedQueue.elementRetrieved'){
        // task is retrieved from the queue
        var task = evt.getData();
        var queue = evt.getTarget();
        // execute the task
        var result = task.execute();
        // notify the producer that the task is done
        queue.notify('synchronizedQueue.taskDone', result);
        if(queue.remainingElements()>0){
          // at least one task is still in the queue, take it
          queue.take();
        }
      }
    });
  });
  var threadListener = $.new(_.Consumer);
  // global listener for the synchronizedQueue.ready event 
  // triggered when the synchronzied queue is synchronized with this thread
  $.on('synchronizedQueue.ready', threadListener);
});

实现自定义逻辑的任务类

OODK('project', function($, _){
  $.public().implements(OODK.foundation.Serializable).class(function Task($, µ, _){
    // the array chunk to calculate
    $.private('chunk');
    $.public(function __initialize(chunk){
      _.chunk = chunk;
    });
    // calculate the sum of all entries of a chunk
    // implements the custom logic here
    $.public(function execute(){
      var result = 0;
      for(var i=0; i<_.chunk.length; i++){
        result += _.chunk[i];
      }
      return result;
    });
    $.static(function($, µ, _){
      $.public(function factory(chunk){
        var task = $.new($.ns.Task, chunk);
        return task;
      });
    });
  });
});