javascript 中的 setTimeout 没有给浏览器“喘息空间”

发布于 2024-08-25 12:11:24 字数 3111 浏览 10 评论 0原文

好吧,我以为我已经完成了整个 setTimeout 事情,但我似乎大错特错了。

我正在使用 excanvas 和 javascript 绘制我家乡的地图,但是绘制过程会阻塞浏览器。现在我被迫迎合 IE6,因为我在一个大组织中,这可能是缓慢的一个重要原因。

所以我想我要做的是构建一个名为distributedDrawPolys的过程(我可能在那里使用了错误的词,所以不要关注分布式这个词),它基本上从全局数组中弹出多边形以绘制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 方法将允许站点按组绘制多边形,以便用户能够在页面仍在绘制时与页面进行交互。我在这里做错了什么?

Alright, I thought I had this whole setTimeout thing perfect but I seem to be horribly mistaken.

I'm using excanvas and javascript to draw a map of my home state, however the drawing procedure chokes the browser. Right now I'm forced to pander to IE6 because I'm in a big organisation, which is probably a large part of the slowness.

So what I thought I'd do is build a procedure called distributedDrawPolys (I'm probably using the wrong word there, so don't focus on the word distributed) which basically pops the polygons off of a global array in order to draw 50 of them at a time.

This is the method that pushes the polygons on to the global array and runs the setTimeout:

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

I put an alert at the end of that method, it runs in practically a second.

The distributed method looks like:

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

Edit: fixed the format

So I thought the setTimeout method would allow the site to draw the polygons in groups so the users would be able to interact with the page while it was still drawing. What am I doing wrong here?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(5

给不了的爱 2024-09-01 12:11:24

如果您的循环运行时间不到一秒,则所有 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。对于你正在使用的东西,你可能会发现你需要调整超时时间,但我倾向于怀疑它——它基本上是一个“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);
        }
    }
}

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

If your loop is running in under a second, all of your setTimeout calls will stack up trying to fire off about five seconds later.

If you want to give the browser breathing room for intermediate rendering, push all of your objects on the stack, then call the function with a limit, and have the function schedule itself when it's done that many objects. Semi-pseudocode:

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

That builds the array all in one go, then triggers the first batch (up to limit) of rendering. At the end of the first batch, if there's more rendering to do, it schedules it to happen about 10ms later. In fact, it will happen no sooner than 10ms later and quite possibly later than that, if the browser is still busy with other things. (Re 10ms: most browsers won't schedule something for sooner than 10ms from now.) (Edit Andy E points out, quite correctly, that you may as well fold the logic related to what needs to be rendered into the rendering function directly rather than first building the array, then processing it. Doesn't change the above much except for the array part, how you do the chaining and the "breathing room" remains the same.)

Not knowing the excanvas stuff you're using, you may find you need to adjust the timeout time, but I tend to doubt it -- it's basically a "yield" operation which lets the browser do some things and come back to you.

Note that the psuedo-code sample above is using what appear to be globals. I wouldn't recommend actually using globals. You might even want to do this instead:

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

...but I didn't like to complicate the main answer by introducing the closure (although technically both codeblocks involve closures).

安静被遗忘 2024-09-01 12:11:24

将代码更改为

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

Change the code to

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
    }
}
七婞 2024-09-01 12:11:24

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

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

No, you want something different.

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

then drawOnePolygon must be something like this:

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

kick it off with:

drawPolys(0); 
淡淡の花香 2024-09-01 12:11:24

如果您每五秒调用一次,并且每次执行 1 秒的工作,则浏览器将有 20% 的时间因交互而阻塞。

您可以尝试将大函数切碎并分块运行,以使体验更加流畅。

If you call it once every five seconds, and it does 1 second of work each time, the browser will be choked for interaction 20% of the time.

You could try and chop up your big function and run it in chunks to make the experience smoother.

最单纯的乌龟 2024-09-01 12:11:24

这没有按预期工作。当您的第一个函数开始执行时,您的全局数组已包含 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
})();

This doesn't work as expected. By the time your first function starts executing, your global array already contains 50 elements. You just end up operating on the same data 50 times.

What you can do is to chain your setTimeout so that the next function executes after the previous method.

Here's a simple implementation:

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
})();
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文