tiempo renderizado recursos quitar que los licencia elimina ejecución costo contenido bloqueen bloquean bloquea javascript

renderizado - Javascript: ¿cómo evitar bloquear el navegador mientras se realiza un trabajo pesado?



quitar el javascript que bloquea el renderizado de contenido wordpress (8)

Tengo una función en mi script JS:

function heavyWork(){ for (i=0; i<300; i++){ doSomethingHeavy(i); } }

Tal vez "doSomethingHeavy" esté bien por sí mismo, pero repetirlo 300 veces hace que la ventana del navegador se atasque durante un tiempo no despreciable. En Chrome no es un problema tan grande porque solo se efectúa una pestaña; pero para Firefox es un completo desastre.

¿Hay alguna manera de decirle al navegador / JS que "se lo tome con calma" y no bloquee todo entre las llamadas para hacer Algo Algo?


Hubo una persona que escribió una biblioteca específica de javascript de la tarea de fondo para hacer un trabajo tan pesado. Puede consultar esta pregunta aquí:

Ejecutar tarea de fondo en Javascript

No lo he usado para mí, solo usé el uso del hilo también mencionado.


Necesitamos liberar el control del navegador de vez en cuando para evitar monopolizar la atención del navegador.

Una forma de liberar el control es usar un setTimeout , que programa una "devolución de llamada" para ser llamada en algún período de tiempo. Por ejemplo:

var f1 = function() { document.body.appendChild(document.createTextNode("Hello")); setTimeout(f2, 1000); }; var f2 = function() { document.body.appendChild(document.createTextNode("World")); };

Llamar a f1 aquí agregará la palabra hello a su documento, programará un cálculo pendiente y luego liberará el control en el navegador. Eventualmente, se llamará f2 .

Tenga en cuenta que no es suficiente esparcir setTimeout indiscriminada en todo el programa como si fuera polvo mágico de duendes: realmente necesita encapsular el resto del cálculo en la devolución de llamada. Normalmente, el setTimeout será la última cosa en una función, con el resto de los cálculos rellenos en la devolución de llamada.

Para su caso particular, el código debe transformarse cuidadosamente en algo como esto:

var heavyWork = function(i, onSuccess) { if (i < 300) { var restOfComputation = function() { return heavyWork(i+1, onSuccess); } return doSomethingHeavy(i, restOfComputation); } else { onSuccess(); } }; var restOfComputation = function(i, callback) { // ... do some work, followed by: setTimeout(callback, 0); };

que liberará el control al navegador en cada restOfComputation .

Como otro ejemplo concreto de esto, vea: ¿Cómo puedo poner en cola una serie de clips de sonido HTML5 <audio> para reproducir en secuencia?

Los programadores avanzados de JavaScript deben saber cómo realizar esta transformación de programa o, de lo contrario, se enfrentarán a los problemas que se le presenten. Descubrirá que si utiliza esta técnica, tendrá que escribir sus programas en un estilo peculiar, donde cada función que puede liberar el control acepta una función de devolución de llamada. El término técnico para este estilo es "estilo de paso de continuación" o "estilo asíncrono".



Puede intentar setTimeout cada llamada de función en un setTimeout , con un tiempo de espera de 0. Esto empujará las llamadas a la parte inferior de la pila, y debería permitir que el navegador descanse entre cada una.

function heavyWork(){ for (i=0; i<300; i++){ setTimeout(function(){ doSomethingHeavy(i); }, 0); } }

EDIT: Me acabo de dar cuenta de que esto no funcionará. El valor i será el mismo para cada iteración de bucle, debe realizar un cierre.

function heavyWork(){ for (i=0; i<300; i++){ setTimeout((function(x){ return function(){ doSomethingHeavy(x); }; })(i), 0); } }


Puedes anidar tus llamadas dentro de una llamada setTimeout :

for(...) { setTimeout(function(i) { return function() { doSomethingHeavy(i); } }(i), 0); }

Esto pone en cola las llamadas a doSomethingHeavy para su ejecución inmediata, pero otras operaciones de JavaScript se pueden colocar entre ellas.

Una mejor solución es que el navegador genere un nuevo proceso de no bloqueo a través de los Trabajadores Web , pero eso es específico de HTML5.

EDITAR:

El uso de setTimeout(fn, 0) realidad toma mucho más tiempo que cero milisegundos: Firefox, por ejemplo, impone un tiempo de espera mínimo de 4 milisegundos . Un mejor enfoque podría ser usar setZeroTimeout , que prefiere postMessage para la postMessage instantánea de funciones con posibilidad de interrupción, pero usar setTimeout como setTimeout para los navegadores más antiguos.


Puedes hacer muchas cosas:

  1. optimice los bucles: si el trabajo pesado tiene algo que ver con el acceso a DOM, vea esta answer
    • Si la función está funcionando con algún tipo de uso de datos en bruto, se escriben matrices MSDN MDN
  2. El método con setTimeout () se llama eteration . Muy útil.

  3. La función parece ser muy sencilla en typicall para lenguajes de programación no funcionales. JavaScript gana ventaja de callbacks SO pregunta .

  4. Una nueva característica es el de los trabajadores web https://developer.mozilla.org/En/Using_web_workers MSDN wikipedia .

  5. lo último (quizás) es combinar todos los métodos, con la forma tradicional en que la función usa solo un hilo. Si puede utilizar los trabajadores web, puede dividir el trabajo entre varios. Esto debería minimizar el tiempo necesario para terminar la tarea.


Veo dos maneras:

a) Se le permite utilizar la característica Html5. Entonces puedes considerar usar un hilo de trabajo.

b) Divide esta tarea y pone en cola un mensaje que solo hace una llamada a la vez e itera siempre que haya algo que hacer.


function doSomethingHeavy(param){ if (param && param%100==0) alert(param); } (function heavyWork(){ for (var i=0; i<=300; i++){ window.setTimeout( (function(i){ return function(){doSomethingHeavy(i)}; })(i) ,0); } }())