setTimeout в javascript не дает браузеру «передышку» - PullRequest
3 голосов
/ 16 марта 2010

Хорошо, я думал, что у меня все отлично с setTimeout, но я, кажется, ужасно ошибаюсь.

Я использую excanvas и javascript, чтобы нарисовать карту моего домашнего состояния, однако процедура рисования душит браузер. Прямо сейчас я вынужден потворствовать IE6, потому что я нахожусь в большой организации, что, вероятно, является большой частью медлительности.

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

Это метод, который помещает полигоны в глобальный массив и запускает setTimeout:

 for (var x = 0; x < polygon.length; x++) {
      coordsObject.push(polygon[x]);
      fifty++;
      if (fifty > 49) {
           timeOutID = setTimeout(distributedDrawPolys, 5000);
           fifty = 0;
      }
 }

Я поставил предупреждение в конце этого метода, он запускается практически за секунду.

Распределенный метод выглядит так:

 function distributedDrawPolys()
 {
      if (coordsObject.length > 0) {
           for (x = 0; x < 50; x++) { //Only do 50 polygons
                var polygon = coordsObject.pop();
                var coordinate = polygon.selectNodes("Coordinates/point");
                var zip = polygon.selectNodes("ZipCode");
                var rating = polygon.selectNodes("Score");
                if (zip[0].text.indexOf("HH") == -1) {
                     var lastOriginCoord = [];
                     for (var y = 0; y < coordinate.length; y++) {
                          var point = coordinate[y];
                          latitude = shiftLat(point.getAttribute("lat"));
                          longitude = shiftLong(point.getAttribute("long"));
                          if (y == 0) {
                               lastOriginCoord[0] = point.getAttribute("long");
                               lastOriginCoord[1] = point.getAttribute("lat");
                          }
                          if (y == 1) {
                               beginPoly(longitude, latitude);
                          }
                          if (y > 0) {
                               if (translateLongToX(longitude) > 0 && translateLongToX(longitude) < 800 && translateLatToY(latitude) > 0 && translateLatToY(latitude) < 600) {
                                    drawPolyPoint(longitude, latitude);
                               }
                          }
                     }
                     y = 0;
                     if (zip[0].text != targetZipCode) {
                          if (rating[0] != null) {
                               if (rating[0].text == "Excellent") {
                                    endPoly("rgb(0,153,0)");
                               }
                               else if (rating[0].text == "Good") {
                                    endPoly("rgb(153,204,102)");
                               }
                               else if (rating[0].text == "Average") {
                                    endPoly("rgb(255,255,153)");
                               }
                          }
                          else { endPoly("rgb(255,255,255)"); }
                     }
                     else {
                     endPoly("rgb(255,0,0)");
                     }
                }
           }
      }
 }

Редактировать: исправлен формат

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

Ответы [ 5 ]

7 голосов
/ 16 марта 2010

Если ваш цикл выполняется менее чем за секунду, все ваши setTimeout вызовы будут складываться, пытаясь сработать примерно через пять секунд.

Если вы хотите дать браузеру передышку для промежуточного рендеринга, поместите все свои объекты в стек, затем вызовите функцию с ограничением и получите расписание самой функции, когда будет выполнено столько объектов. Пол-псевдокод:

var theArray = [];
var limit = 50;

function showStuff() {
    for (...) {
        // push objects on theArray
    }

    renderArrayInBatches();
}

function renderArrayInBatches() {
    var counter;

    for (counter = limit; counter > 0; --counter) {
        // pop an object and render it
    }
    if (theArray.length > 0) {
        setTimeout(renderArrayInBatches, 10);
    }
}

Это создает массив за один раз, а затем запускает первый пакет (до limit) рендеринга. В конце первого пакета, если есть еще рендеринг, он планирует это сделать примерно через 10 мс. На самом деле, это произойдет не раньше , чем через 10 мс и, возможно, позже, если браузер все еще занят другими вещами. (Re 10 мс: большинство браузеров не планируют что-либо на более чем 10 мс.) ( Edit Andy E совершенно правильно указывает, что вы также можете свернуть логику, связанную с тем, что нужно визуализировать непосредственно в функцию рендеринга, а не сначала собирать массив, а затем обрабатывать его. Ничего особо не изменится, за исключением части массива, то, как вы выполняете цепочку и «передышку», остается прежним.)

Не зная, что вы используете в excanvas, вам может понадобиться настроить время ожидания, но я склонен сомневаться в этом - это в основном операция «yield», которая позволяет браузеру делать некоторые вещи и возвращаться к ним. вы.

Обратите внимание, что приведенный выше пример псевдокода использует то, что выглядит глобально. Я бы не советовал использовать глобалы. Вы могли бы даже хотеть сделать это вместо этого:

function showStuff() {
    var theArray = [];
    var limit = 50;

    for (...) {
        // push objects on theArray
    }

    renderArrayInBatches();

    function renderArrayInBatches() {
        var counter;

        for (counter = limit; counter > 0; --counter) {
            // pop an object and render it
        }
        if (theArray.length > 0) {
            setTimeout(renderArrayInBatches, 10);
        }
    }
}

... но мне не хотелось усложнять основной ответ введением замыкания (хотя технически оба кодовых блока включают замыкания).

3 голосов
/ 16 марта 2010

Измените код на

for (var x = 0; x < polygon.length; x++) {
    coordsObject.push(polygon[x]);
}
distributedDrawPolys();

function distributedDrawPolys()
{
    if (coordsObject.length > 0) {
        for (x = 0; x < 50; x++) {
            ...
        }
        setTimeout("distributedDrawPolys()", 5000); //render next 50 polys in 5 sec
    }
}
1 голос
/ 16 марта 2010

Нет, вы хотите что-то другое.

var batchsize = 50; 
function drawPolys(start) {
    for (var x = start; x < polygon.length; x++) {
        drawOnePolygon(polygon[x]);
        if (start + batchsize <= x) {
            // quit this invocation, and schedule the next
            setTimeout(function(){drawPolys(x+1);}, 400);
            return;  // important
        }
    }
}

тогда drawOnePolygon должен быть примерно таким:

function drawOnePolygon(p) {
    var coordinate = polygon.selectNodes("Coordinates/point");
    //...and so on, continuing from your code above.
}

Начните с:

drawPolys(0); 
0 голосов
/ 16 марта 2010

Если вы вызываете его один раз каждые пять секунд, и каждый раз он выполняет 1 секунду работы, браузер будет подавлен для взаимодействия в 20% случаев.

Вы можете попытаться нарезать свою большую функцию и запустить ее по частям, чтобы сделать процесс более плавным.

0 голосов
/ 16 марта 2010

Это не работает, как ожидалось. К тому времени, когда ваша первая функция начинает выполняться, ваш глобальный массив уже содержит 50 элементов. Вы просто работаете с одними и теми же данными 50 раз.

Что вы можете сделать, это связать ваш setTimeout так, чтобы следующая функция выполнялась после предыдущего метода.

Вот простая реализация:

var coordObj = [...]; //50 or whatever elements
(function() {
    if (coordObj.length === 0) return; //Guardian
    var obj = coordObj.pop(); //or .shift(), based on the order desired.
    doStuffWithCoordObj(obj);
    setTimeout(arguments.callee,0); //call this anonymous function after a timeout
})();
...