Skip to content Skip to sidebar Skip to footer

Prevent Browser Freezing And Crashing For Long Time Calculation

I need check in my database names who are duplicated and change this name to avoid duplicates. I using script suggested by @Jefré N. function eliminateDuplicates() { var re

Solution 1:

I think that the most cumbersome part of your code is the DOM access: getting input values and updating them.

According to the webworkers documentation, webworkers have their limitations and one of them is DOM manipulation. So I'd discard that option.

In order to fix things, I'd do as follows:

  1. Improve your eliminateDuplicates algorithm (make it faster).
  2. Make eliminateDuplicates asynchronous: divide the set of elements in smaller ones and perform each calculation in a different event loop tick (setTimeout).

Here I present you a solution I've come up with. Hope it gives you some ideas and help you to solve your problem.

First, I tweaked a bit eliminateDuplicates (I called it 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];
        }
    }
}

I avoided using jQuery inside the main loop because its wrapper makes things slower and in your case it wasn't worth using it. These small changes improved performance in 100ms per 10.000 elements (give it or take).

I created two functions that use modifyDOM: one asynchronous and other synchronous.

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);
}

Lastly and in order to test everything, a function that executes when the DOM is ready and creates 10.000 inputs. It then outputs how long it takes to run any of the above methods.

$(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);
});

Here you have the fiddle to test it all.


Solution 2:

here is a solution using OODK-JS to calculate the sum of an array of 1.000.000 entries through webworkers.

This solution implements the producer/consumer design pattern using the SynchronizedQueue foundation class: the producer (main thread) generate a task for each chunk of the array and add it to queue. The consumer (webworker) take a task in the queue and execute it until no one left. Once all tasks are executed, the producer display the final result

// 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 ...');
          });

The consumer (webworker)

//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);

});

The task class to implement the custom logic

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;
      });
    });
  });

});

Post a Comment for "Prevent Browser Freezing And Crashing For Long Time Calculation"