2009-03-23 12 views
50

Tengo JavaScript que realiza una gran cantidad de cálculos, así como leer/escribir valores desde/hacia el DOM. La página es enorme, por lo que a menudo termina bloqueando el navegador por hasta un minuto (a veces más tiempo con IE) con un 100% de uso de la CPU.Impedir javascript de ejecución prolongada bloquear el navegador

¿Hay algún recurso en la optimización de JavaScript para evitar que esto suceda (todo lo que puedo encontrar es cómo desactivar la advertencia de script de larga duración de Firefox)?

Respuesta

44

si puede convertir su algoritmo de cálculo en algo que pueda llamarse iterativamente, puede liberar el control del navegador a intervalos frecuentes usando setTimeout con un valor de tiempo de espera corto.

Por ejemplo, algo como esto ...

function doCalculation() 
{ 
    //do your thing for a short time 

    //figure out how complete you are 
    var percent_complete=.... 

    return percent_complete; 
} 

function pump() 
{ 
    var percent_complete=doCalculation(); 

    //maybe update a progress meter here! 

    //carry on pumping? 
    if (percent_complete<100) 
    { 
     setTimeout(pump, 50); 
    } 
} 

//start the calculation 
pump(); 
+2

¿Sabes si esto todavía funcionaría si usas 'setTimeout (pump, 0)'? ¿O esto posiblemente mantendría presionado el código del navegador que responde a la entrada del mouse, actualiza el medidor de progreso u otros elementos DOM? – Andy

+0

@Andy Sí 'setTimeout' con 0 también ayudará. Vea algunas de las respuestas a [esta pregunta] (https://stackoverflow.com/questions/779379/why-is-settimeoutfn-0-sometimes-useful). – ShreevatsaR

1

Puede intentar realizar cálculos de larga ejecución en subprocesos (consulte JavaScript and Threads), aunque no son muy portátiles.

También puede intentar usar algún generador de perfiles de Javascript para encontrar cuellos de botella de rendimiento. Firebug admite la creación de perfiles javascript.

7

Use los tiempos de espera.

Poniendo el contenido de sus bucles en funciones separadas, y llamándolos desde setTimeout() con un tiempo de espera de 50 o más, el javascript cederá el control del hilo y volverá más tarde, permitiendo el UI para obtener un look-in.

Hay un buen workthrough here.

2

había blogueado hace alrededor de in-browser performance algún tiempo, pero vamos a resumir las relacionadas con el DOM para usted aquí.

  • Actualice el DOM con la menor frecuencia posible. Realice los cambios en los objetos DOM en memoria y añádalos solo una vez al DOM.
  • Use innerHTML. Es más rápido que los métodos DOM en la mayoría de los navegadores.
  • Use la delegación de eventos en lugar del manejo regular de eventos.
  • Sepa qué llamadas son caras y evítelas. Por ejemplo, en jQuery, $ ("div.className") será más caro que $ ("# someId").

A continuación hay algunos relacionados con la misma JavaScript:

  • Loop lo menos posible. Si tiene una función que recopila nodos DOM, y otra que los procesa, está bucleando dos veces. En su lugar, transfiera una función anónima a la función que recopila los nodos y procese los nodos a medida que los vaya recopilando.
  • Utilice la funcionalidad nativa cuando sea posible. Por ejemplo, para cada iterador.
  • Use setTimeout para dejar que el navegador respire de vez en cuando.
  • Para funciones costosas que tienen salidas idempotentes, guarde en caché los resultados para que no tenga que recalcularlos.

Hay más en mi blog (enlace arriba).

1

Esto todavía está un poco desangrado, pero Firefox 3.5 tiene estas cosas llamadas Web Workers, aunque no estoy seguro de su soporte en otros navegadores.

Sr. Resig tiene un artículo sobre ellos aquí: http://ejohn.org/blog/web-workers/

Y el Simulated Annealing es probablemente el ejemplo más simple de que, si notará el logo de Firefox girar no se congela hasta, cuando los subprocesos de trabajo están haciendo sus solicitudes (por lo tanto, no congelar el navegador).

+0

¿Por qué los trabajadores de la web no son la respuesta aquí? –

+0

Bueno, yo respondí esto en 09 'así que tal vez no fue tan usado entonces ... – leeand00

+0

Buen punto, bueno, ciertamente debería estar arriba ahora. El uso de setTimeout me parece tonto sin importar lo que hagas a menos que realmente necesites un tiempo de espera para algo. Si vas a codepen.io en Chrome Windows ahora mismo y haces un algoritmo con decir complejidad O (N!) Como encontrar todas las permutaciones de la cadena "ABCDEFGHIJKLMNOP", tu navegador se bloqueará y dejará de responder. En un hilo de trabajo, la IU sigue funcionando por sí misma. Esta es claramente la respuesta correcta. –

1

Mi experiencia es que la manipulación DOM, especialmente en IE, es mucho más un problema para el rendimiento que el JavaScript "core" (bucle, etc.).

Si está construyendo nodos, es mucho más rápido en IE hacerlo creando una cadena HTML y luego configurando innerHTML en un contenedor que usando métodos DOM como createElement/appendChild.

0

usted podría tratar de acortar el código por

$(xmlDoc).find("Object").each(function(arg1) { 
    (function(arg1_received) { 
       setTimeout(function(arg1_received_reached) { 

        //your stuff with the arg1_received_reached goes here 

       }(arg1_received), 0) 
      })(arg1) 
}(this)); 

o "para" bucles tratar

for (var i = 0 ; i < 10000 ; i = i + 1) { 
    (function(arg1_received) { 
     setTimeout(function(arg1_received_reached) { 

      //your stuff with the arg1_received_reached goes here 

     }(arg1_received), 0) 
    })(arg1_to_send) 
} 

que tenía el mismo problema y mis clientes estaba informando esto como error "Matar página". Pero ahora juz tengo la mejor solución para eso. :)

Cuestiones relacionadas