2016-06-21 4 views
2

У меня есть набор данных, по которому я повторяю и выполняю довольно тяжелую операцию над каждым элементом. Чтобы предотвратить зависание браузера, я установил тайм-аут 0 мс. Хотя браузер не замерзает, расчет может занять несколько секунд в зависимости от пользовательского ввода. Вот код:Ломать петлю снаружи

function calculation(){ 
    $.each(data,function(key,value){ 
     setTimeout(function(){ 
      doTheHeavyLifting(value); 
     },0); 
    }); 
} 

Теперь проблема в том, что я хочу, чтобы остановить предыдущие вычисления, если пользователь запрашивает, чтобы запустить расчет снова с новыми значениями.

Я попытался определение continueCalculating булева вне функции, и установив, что false в runCalculation() перед вызовом calculation(), и внутри calculation() сброса его обратно в true. Вот измененный код:

var continueCalculating=false; 

function runCalculator(){ 
    continueCalculating=false; 
    calculation(); 
} 

function calculation(){ 
    continueCalculating=true; 
    $.each(data,function(key,value){ 
     setTimeout(function(){ 
      if(continueCalculating){ 
       doTheHeavyLifting(value); 
      }else{ 
       console.log("Abort the mission!"); 
       return false; 
      } 
     },0); 
    }); 
} 

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

var continueCalculating=false; 
var operationQueue=[]; 

function runCalculator(){ 
    continueCalculating=false; 
    $.each(operationQueue, function(k,v){ 
     clearTimeout(v); 
    }); 
    calculation(); 
} 

function calculation(){ 
    continueCalculating=true; 
    $.each(data,function(key,value){ 
     operationQueue.push(setTimeout(function(){ 
      if(continueCalculating){ 
       doTheHeavyLifting(value); 
      }else{ 
       console.log("Abort the mission!"); 
       return false; 
      } 
     },0)); 
    }); 
} 

Ну, это тоже не дало результатов.

Я знаю, что $.each медленнее, чем for, но есть только около двух десятков пунктов, чтобы перебрать, так что, конечно, это не шея бутылки. Я не ищу способы оптимизировать этот код, мне просто нужно остановить текущий расчет. Это возможно?

+1

Вы не должны использовать петлю здесь вообще. Если вы хотите сделать обработку элементов отменными, вы должны обработать один элемент за раз и «дать время» между ними для другого кода, чтобы прервать процесс. Вы в основном хотите рекурсивную функцию, которая использует 'setTimeout'. –

ответ

2

Вы должны перебирать асинхронно:

var data = [0,1,2,3,4,5,6,7,8,9,"end"]; 
 
function doTheHeavyLifting(value) { 
 
    console.log(value); 
 
} 
 
function calculation(data) { 
 
    var key = 0, timer; 
 
    (function loop() { 
 
    if(key < data.length) { 
 
     doTheHeavyLifting(data[key]); 
 
     key++; 
 
     timer = setTimeout(loop, 1e3); 
 
    } 
 
    })(); 
 
    return {stop: function() { clearTimeout(timer) } }; 
 
} 
 
var calc = calculation(data); 
 
document.querySelector('#stop').onclick = calc.stop;
<button id="stop">Stop</button>

+0

Это действительно здорово. Я всегда использовал глобальный флаг, который функция async периодически проверяет, чтобы выполнить что-то подобное, но здесь вы избегаете как загрязнения глобальной области, так и проверки флажка во время каждого цикла, и ваш пример ясен и прост в использовании. +1 – chiliNUT

0

Вы можете использовать $.queue(), $.Deferred(), .promise()

// check at `if` condition within deferred 
 
var stop = false; 
 
$("button").on("click", function() { 
 
    // clear `"calculation"` queue at `click` event at `button` element 
 
    $.queue(data, "calculation", []); 
 
    // set `stop` to `true` at `click` event at `button` 
 
    stop = true; 
 
}); 
 
// `data` 
 
var data = Array(100).fill(0).map((v, k) => k); 
 

 
function doTheHeavyLifting(val) { 
 
    console.log(val); 
 
    // return `.promise()` when process within `doTheHeavyLifting` completes 
 
    return $("body").append("\n" + val).promise() 
 
} 
 

 
function calculation() { 
 
    $.queue(data, "calculation", $.map(data, function(value, index) { 
 
    return function(next) {  
 
     return new $.Deferred(function(dfd) { 
 
     setTimeout(function() { 
 
      if (!stop) { 
 
      dfd.resolve(doTheHeavyLifting(value)) 
 
      } else { 
 
      dfd.rejectWith(
 
       $.queue(data, "calculation") 
 
       , ["queue stopped at " + index] 
 
      ); 
 
      } 
 
     },0) 
 
     }) 
 
     // iterate next item in `data` 
 
     .then(next) 
 
     // do stuff if `"calculation"` queue is empty array 
 
     .fail(function(message) { 
 
     console.log(message 
 
        , "calculation queue:", this); 
 
     alert(message);    
 
     }) 
 
    } 
 
    })); 
 
    // start queue 
 
    $.dequeue(data, "calculation"); 
 
} 
 

 
calculation();
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"> 
 
</script> 
 
<button>stop calculation</button>

Смежные вопросы