проблема с рекурсивной функцией и setTimeout()

#javascript #canvas #settimeout

#javascript #холст #settimeout

Вопрос:

У меня есть скрипт, который рисует кучу строк на canvas, но это довольно интенсивно, поэтому во время рендеринга браузер зависает на несколько секунд. Я добавил setTimeout (), чтобы браузер не зависал, и это фактически испортило мой скрипт. Сложно объяснить как, поэтому у меня есть два примера онлайн:

Без setTimeout() :http://www.modwebsolutions.com/test1

С помощью setTimeout() :http://www.modwebsolutions.com/test2

Обратите внимание, что я меняю только одну строку во всем скрипте, то есть строку 69:
без setTimeout(): vLoop();
с помощью setTimeout(): setTimeout(vLoop,1);

Комментарии:

1. можете ли вы указать нам фактический код vLoop

Ответ №1:

Проблема здесь, как намекали другие, заключается в том, что вы рисуете линии по квадранту за раз. Как только SetTimeout метод вызывается и первый vLoop возвращает результат, код переходит к следующему drawVertical , который изменяет все глобальные переменные и так далее.

Что вам нужно сделать, так это синхронизировать то, как вы вызываете vLoop и как вы меняете глобальные переменные.

В принципе, это решение:

 

Заменить …

 drawVertical(c,-1*step,-1*stepInt,-1*bigStep,xStart,xEnd,y/2 50,y);
drawVertical(c,step,stepInt,bigStep,xStart,xEnd,y/2 50,y);
drawVertical(c,-1*step,-1*stepInt,-1*bigStep,xStart,xEnd,y/2-50,0); 
drawVertical(c,step,stepInt,bigStep,xStart,xEnd,y/2-50,0);
  

… с помощью …

 var q = new Array();
q[0] = [c,-1*step,-1*stepInt,-1*bigStep,xStart,xEnd,y/2 50,y];
q[1] = [c,step,stepInt,bigStep,xStart,xEnd,y/2 50,y];
q[2] = [c,-1*step,-1*stepInt,-1*bigStep,xStart,xEnd,y/2-50,0];
q[3] = [c,step,stepInt,bigStep,xStart,xEnd,y/2-50,0];

drawQuadrant(q, 0);
  

 

Замените вашу drawVertical функцию на …

 function drawQuadrant(q, i)
{
    var r = q[i];

    c__ = r[0];
    step__ = r[1];
    stepInt__ = r[2];
    bigStep__ = r[3];
    xStart__ = r[4];
    xEnd__ = r[5];
    yStart__ = r[6];
    yEnd__ = r[7]; 


    vLoop(q,i);
}
  

 

измените vLoop прототип функции, чтобы он выглядел следующим образом …

 function vLoop(q,i)
  

 

и, наконец, замените свой рекурсивный vLoop вызов (изнутри vLoop) на …

 if ((xStart__ > 0) amp;amp; (xStart__ < window.innerWidth))
{
    setTimeout( function(){vLoop(q,i)}, 1 );
}
else if (  i < 4)
{
    setTimeout( function(){drawQuadrant(q,i)}, 1 );
}
  

В последнем блоке это гарантирует, что квадранты не переступают друг через друга.

Ответ №2:

 drawVertical(c,-1*step,-1*stepInt,-1*bigStep,xStart,xEnd,y/2 50,y);
drawVertical(c,step,stepInt,bigStep,xStart,xEnd,y/2 50,y);
drawVertical(c,-1*step,-1*stepInt,-1*bigStep,xStart,xEnd,y/2-50,0); 
drawVertical(c,step,stepInt,bigStep,xStart,xEnd,y/2-50,0);
  

Вы вызываете сразу 4 рекурсивные функции vLoop . Проблема здесь в том, что setTimeout неблокируется там, где блокируется рекурсия as. Итак, в принципе, теперь у вас есть все 4 из этих обратных функций, работающих параллельно, а не последовательно.

Другая проблема заключается в том, что все 4 ссылаются и путаются с глобальным состоянием, и вся ваша программа ломается.

Комментарии:

1. Они все еще однопоточные, поэтому сказать, что они выполняются параллельно, не совсем точно.

2. @NickCraver Выполнение с разделением по времени фактически выполняется параллельно. Чем использование setTimeout отличается от разделения по времени?

3. @Raynos — Вы никогда не будете внутри этого метода более одного раза одновременно, что является определением parallel 🙂

4. @NickCraver но вы будете находиться внутри набора функций, выполняемых рекурсивно более одного раза за раз. Отдельная функция не выполняется параллельно, но рекурсивное выполнение выполняется.

5. @Raynos — Это не’t…at ни одна строка кода, которую он запускает, не будет выполняться одновременно

Ответ №3:

Происходит то, что setTimeout() задерживает все это выполнение на более поздний срок. К сожалению, к этому моменту все ваши глобальные переменные переместились на свои конечные позиции из начального цикла, поскольку он завершился до того, как была нарисована первая строка.

Если бы вы увеличили время ожидания (чтобы на используемые вами общие переменные не влияли до времени отрисовки), вы могли бы достичь желаемого, например:

 setTimeout(function() {
    drawVertical(c,-1*step,-1*stepInt,-1*bigStep,xStart,xEnd,y/2 50,y);
    drawVertical(c,step,stepInt,bigStep,xStart,xEnd,y/2 50,y);
    drawVertical(c,-1*step,-1*stepInt,-1*bigStep,xStart,xEnd,y/2-50,0);
    drawVertical(c,step,stepInt,bigStep,xStart,xEnd,y/2-50,0);
});
  

Тогда это сработало бы (но это опасно, порядок не является абсолютно гарантированным!)

Вы можете увидеть рабочий пример здесь.

Комментарии:

1. Это просто откладывает весь процесс рисования на потом. Как только блок начинает выполняться, у вас по-прежнему возникает первоначальная проблема зависания браузера.

2. @Musaul — Это будет при обратном вызове по времени, но вы можете смягчить это, указав для продолжительности тайм-аута значение, отличное от 0, умножив его на i , например, чтобы разделить их на промежутки.