代码之家  ›  专栏  ›  技术社区  ›  C Bauer

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

  •  3
  • C Bauer  · 技术社区  · 16 年前

    好吧,我以为我把整个设置超时的事情做得很完美,但我似乎错得很厉害。

    我正在使用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方法将允许站点分组绘制多边形,这样用户就可以在页面仍在绘制时与页面交互。我在这里做错什么了?

    5 回复  |  直到 16 年前
        1
  •  3
  •   jitter    16 年前

    将代码更改为

    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
        }
    }
    
        2
  •  7
  •   T.J. Crowder    16 年前

    如果你的循环不到一秒钟,所有的 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 10ms:从现在开始,大多数浏览器不会在超过10毫秒的时间内安排某些内容。)( 编辑 安迪E非常正确地指出,您也可以将与需要呈现的内容相关的逻辑直接折叠到呈现函数中,而不是先构建数组,然后再对其进行处理。除了数组部分,上面的变化不大,链接和“呼吸空间”的方式保持不变。)

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

    请注意,上面的psuedo代码示例使用的是看似全局的代码。我不推荐使用globals。你甚至可以这样做:

    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
  •  1
  •   Cheeso    16 年前

    不,你想要不同的东西。

    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); 
    
        4
  •  0
  •   npup    16 年前

    如果你每5秒调用一次,每次只执行1秒的工作,那么20%的时间浏览器都会因交互而窒息。

    你可以试着把你的大函数切碎,然后分成块运行,这样你的体验会更流畅。

        5
  •  0
  •   Chetan S    16 年前

    这不是预期的效果。当第一个函数开始执行时,全局数组已经包含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
    })();
    
    推荐文章