2016-09-17 55 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)'不適用於我。 –