3

好吧,我以为我已经把整个 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 方法将允许网站分组绘制多边形,这样用户就可以在页面仍在绘制时与页面进行交互。我在这里做错了什么?

4

5 回答 5

7

如果您的循环在一秒钟内运行,您的所有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 毫秒的内容。)(编辑Andy E 非常正确地指出,您不妨将与需要渲染的内容相关的逻辑直接折叠到渲染函数中,而不是比首先构建数组,然后处理它。除了数组部分之外,上面没有太大变化,你如何进行链接和“呼吸室”保持不变。)

不知道你正在使用的 excanvas 东西,你可能会发现你需要调整超时时间,但我倾向于怀疑它——它基本上是一个“让步”操作,它让浏览器做一些事情并返回给你。

请注意,上面的伪代码示例使用的似乎是全局变量。我不建议实际使用全局变量。您甚至可能想要这样做:

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

...但是我不喜欢通过引入闭包来使主要答案复杂化(尽管从技术上讲,两个代码块都涉及闭包)。

于 2010-03-16T14:09:04.693 回答
3

将代码更改为

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
    }
}
于 2010-03-16T14:09:13.363 回答
1

不,你想要一些不同的东西。

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); 
于 2010-03-16T14:12:34.800 回答
0

这不能按预期工作。当您的第一个函数开始执行时,您的全局数组已经包含 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
})();
于 2010-03-16T14:10:09.213 回答
0

如果你每 5 秒调用一次,并且它每次工作 1 秒,浏览器将有 20% 的时间被阻塞以进行交互。

您可以尝试拆分您的大功能并分块运行,以使体验更流畅。

于 2010-03-16T14:23:35.670 回答