2010-03-16 17 views
3

Très bien, je pensais que j'avais tout ce setTimeout chose parfaite, mais je semble être horriblement trompé. J'utilise excanvas et javascript pour dessiner une carte de mon état d'origine, mais la procédure de dessin étouffe le navigateur. À l'heure actuelle, je suis obligé de me tourner vers IE6 parce que je suis dans une grande organisation, ce qui est probablement une grande partie de la lenteur. Donc, ce que je pensais faire est de construire une procédure appelée distributedDrawPolys (j'utilise probablement le mauvais mot là-bas, donc ne vous concentrez pas sur le mot distribué) qui fait ressortir les polygones d'un tableau global afin de dessiner 50 d'entre eux à la fois.setTimeout en javascript ne donnant pas le navigateur «salle de respiration»

Ceci est la méthode qui pousse les polygones sur le tableau global et exécute le setTimeout:

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

Je mis une alerte à la fin de cette méthode, il fonctionne dans pratiquement une seconde.

La méthode distribuée ressemble:

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: fixe le format

donc je pensais que la méthode setTimeout permettrait au site de dessiner les polygones en groupes afin que les utilisateurs seraient en mesure d'interagir avec la page alors qu'il dessinait encore. Qu'est-ce que je fais mal ici?

Répondre

3

Changer le code à

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

Le navigateur est toujours verrouillé quand je lance le code comme ceci:/(Je mets aussi le settimeout à l'intérieur de l'instruction if) –

+0

Oups désolé. J'ai oublié les guillemets. Essayez à nouveau s'il vous plaît – jitter

+0

EDIT - NVM Je suis un idiot! Merci!!! –

7

Si votre boucle s'exécute en moins d'une seconde, tous vos appels setTimeout s'empilent en tentant de se déclencher environ cinq secondes plus tard. Si vous voulez donner au navigateur une marge de manœuvre pour le rendu intermédiaire, poussez tous vos objets sur la pile, puis appelez la fonction avec une limite, et faites en sorte que la fonction se programme elle-même quand elle a fait autant d'objets. 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); 
    } 
} 

qui construit le tableau en une seule fois, déclenche alors le premier lot (jusqu'à limit) de rendu. À la fin du premier lot, s'il y a plus de rendu à faire, il planifie qu'il se produise environ 10 ms plus tard. En fait, il n'arrivera pas plus tôt que 10ms plus tard et peut-être plus tard, si le navigateur est encore occupé avec d'autres choses. (Re 10ms: la plupart des navigateurs ne planifient pas quelque chose pour plus de 10ms à partir de maintenant.) (Modifier Andy E souligne, tout à fait correctement, que vous pouvez aussi bien plier la logique liée à ce qui doit être rendu dans la fonction de rendu directement au lieu de construire d'abord le tableau, puis de le traiter.Ce ne change pas beaucoup ce qui précède, sauf pour la partie tableau, comment vous faites le chaînage et la "respiration" reste la même.)

vous utilisez, vous pouvez trouver que vous avez besoin d'ajuster le temps d'attente, mais j'ai tendance à en douter - c'est essentiellement une opération «rendement» qui permet au navigateur de faire certaines choses et de revenir vers vous.

Notez que l'exemple de code de pseudo-code ci-dessus utilise ce qui semble être des globaux. Je ne recommanderais pas d'utiliser des globals. Vous pouvez même le faire à la place:

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

... mais je n'aimais pas compliquer la réponse principale en introduisant la fermeture (bien que techniquement les deux codeblocks impliquent des fermetures).

+0

Bonne réponse. @C Bauer, je dois garder à l'esprit que setTimeout ne bloque pas. Il planifie simplement une fonction à exécuter à un certain moment dans le futur, de sorte que les délais d'attente peuvent s'empiler. –

+2

Je tapais une réponse similaire, donc +1. Une autre option consisterait à supprimer la boucle for et à faire en sorte que chaque appel 'distributedDrawPolys()' définisse le temporisateur pour le prochain intervalle de 10 ms. –

+0

Salut TJ, je voulais juste dire que j'apprécie l'effort que vous avez mis dans cette réponse et, une fois que j'ai compris ce que je m'étais confus à propos de setTimeout en premier lieu (en partie à cause du commentaire d'Andy E), sens. Cependant, je ne serais pas vraiment arrivé sans l'exemple de la gigue. –

0

Cela ne fonctionne pas comme prévu. Au moment où votre première fonction commence à s'exécuter, votre tableau global contient déjà 50 éléments. Vous finissez par utiliser 50 fois les mêmes données.

Ce que vous pouvez faire est de chaîner votre setTimeout afin que la fonction suivante s'exécute après la méthode précédente.

Voici une implémentation simple:

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 
})(); 
+0

La méthode pop supprime effectivement l'objet tableau retourné du global à chaque fois qu'il est exécuté, de sorte que les données ne sont pas réutilisé. –

+0

@C Bauer: Bonne prise, je n'ai pas regardé à l'intérieur de cette fonction. En tout cas, ce n'est pas destiné à être la solution. C'est juste un modèle pour faire de longues mises à jour de l'interface utilisateur non bloquantes. –

+0

Mis à jour avec la bonne approche. –

1

Non, vous voulez quelque chose de différent.

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

alors drawOnePolygon doit être quelque chose comme ceci:

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

le coup d'envoi avec:

drawPolys(0); 
0

Si vous appelez une fois toutes les cinq secondes, et il fait 1 seconde de travail à chaque fois, le navigateur sera étranglé pour une interaction 20% du temps.

Vous pourriez essayer de découper votre grande fonction et l'exécuter en morceaux pour rendre l'expérience plus fluide.