Вопрос

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

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

Итак, что я решил сделать, так это создать процедуру под названием distributedDrawPolys (вероятно, я использую здесь неправильное слово, так что не заостряйте внимание на слове distributed), которая в основном извлекает полигоны из глобального массива, чтобы отрисовывать 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 позволит сайту рисовать полигоны группами, чтобы пользователи могли взаимодействовать со страницей, пока она еще рисуется.Что я здесь делаю не так?

Это было полезно?

Решение

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

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

Другие советы

Если ваш цикл выполняется менее чем за секунду, все ваши 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 мс позже и, вполне возможно, еще позже, если браузер все еще занят другими делами.(Повторно 10 мс:большинство браузеров не будут планировать что-либо раньше, чем через 10 мс с этого момента.) (Редактировать Энди Э совершенно правильно указывает, что вы также можете включить логику, связанную с тем, что должно быть отрисовано, непосредственно в функцию отрисовки, вместо того чтобы сначала создавать массив, а затем обрабатывать его.Это не сильно меняет вышесказанное, за исключением части массива, то, как вы выполняете цепочку, и "передышка" остается прежней.)

Не зная, что за 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);
        }
    }
}

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

Нет, ты хочешь чего-то другого.

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

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

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

Это работает не так, как ожидалось.К тому времени, когда ваша первая функция начинает выполняться, ваш глобальный массив уже содержит 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
})();
Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top