2016-09-17 108 views
2

我需要检查我的数据库名称是否有重复,并更改此名称以避免重复。我使用@建议脚本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. 
    } 

所以脚本工作正常,但如果我有多达上百项。但是,如果我有几千条记录,浏览器就会冻结。 Firefox崩溃了。如何通过添加例如一些加载行o某些时钟指针来防止浏览器冻结和崩溃?也许我需要使用一些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表格中。

有些人建议使用网络工作者。也许这里有人有这种做法,并有一个运作的例子?

+0

'setTImeout'会工作,你的问题是什么?你试过了吗?张贴你已经尝试过,最好在jsfiddle.net并发布链接。 – mikeb

+2

使用webworkers https://developer.mozilla.org/en-US/docs/Web/API/Web_Workers_API/Using_web_workers – rafaelcastrocouto

+0

好的谢谢你的建议。较新的尝试过。也许有些人有良好的工作榜样? –

回答

0

这里是一个解决方案,使用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 ...'); 
      }); 

消费者(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); 

}); 

任务类来实现自定义逻辑

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

}); 
0

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

根据webworkers documentation,网络工作者有其局限性,其中之一就是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主循环里面,因为它的包装使事情变得更慢,你的情况是不值得使用它。这些小小的改变使得每10.000个元素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); 
}); 

这里你已经有fiddle这一必须测试它。

+0

嗨,谢谢你的回答。似乎我错过了一些东西。我可以让它工作。这个函数'函数modifyDOM(elements,repeats)'不适用于我。 –