2009-07-21 16 views
37

Tengo una tarea intensiva de CPU que necesito ejecutar en el cliente. Idealmente, me gustaría poder invocar la función y activar eventos de progreso usando jquery para poder actualizar la UI.Ejecutar fondo Tarea En Javascript

Sé que javascript no admite el enhebrado, pero he visto algunos artículos prometedores que intentan imitar el enhebrado usando setTimeout.

¿Cuál es el mejor enfoque para usar para esto? Gracias.

Respuesta

18

Tuve un problema similar para resolver recientemente en el que necesitaba mantener mi hilo de la IU libre mientras procesaba algunos datos para visualizar.

Escribí una biblioteca Background.js para manejar algunos escenarios: una cola de fondo secuencial (basada en la biblioteca WorkerQueue), una lista de trabajos a los que se llama en cada temporizador y un iterador de matriz para ayudar a dividir su trabaje en trozos más pequeños. Ejemplos y código aquí: https://github.com/kmalakoff/background

¡Disfrútalo!

4

Dependiendo de sus requisitos, puede bajar fácilmente usando Gears. Gears admite subprocesos, que pueden hacer lo que quieras.

Como mencionaste, setTimeout es la otra opción. Dependiendo del tipo de su tarea, puede transferir cada iteración de un ciclo a una llamada setTimeout separada con un espacio intermedio, o puede necesitar separar partes de su algoritmo principal en funciones separadas que se pueden llamar una por una en de la misma manera que llamarías cada iteración.

+0

+1, pero los trabajadores 'Gears no son exactamente lo que otro entorno llama 'hilos'. – Javier

+0

Firefox 3.5 también tiene trabajadores estandarizados W3. Ver mi respuesta para enlaces. – Blixt

1

Mi mejor recomendación es mostrar un simple loading.gif durante la operación. El usuario a menudo acepta algo de duración si se le "ha dicho" que podría llevar algo de tiempo.

Ajaxload - Ajax loading gif generator

35

Básicamente, lo que quiere hacer es dividir la operación en pedazos. Por ejemplo, supongamos que tiene 10 000 elementos que desea procesar, guárdelos en una lista y luego procese un pequeño número de ellos con un pequeño retraso entre cada llamada. He aquí una estructura simple que podría utilizar:

function performTask(items, numToProcess, processItem) { 
    var pos = 0; 
    // This is run once for every numToProcess items. 
    function iteration() { 
     // Calculate last position. 
     var j = Math.min(pos + numToProcess, items.length); 
     // Start at current position and loop to last position. 
     for (var i = pos; i < j; i++) { 
      processItem(items, i); 
     } 
     // Increment current position. 
     pos += numToProcess; 
     // Only continue if there are more items to process. 
     if (pos < items.length) 
      setTimeout(iteration, 10); // Wait 10 ms to let the UI update. 
    } 
    iteration(); 
} 

performTask(
    // A set of items. 
    ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o'], 
    // Process two items every iteration. 
    2, 
    // Function that will do stuff to the items. Called once for every item. Gets 
    // the array with items and the index of the current item (to prevent copying 
    // values around which is unnecessary.) 
    function (items, index) { 
     // Do stuff with items[index] 
     // This could also be inline in iteration for better performance. 
    }); 

También tenga en cuenta que Google Gears has support to do work on a separate thread. Firefox 3.5 también introdujo its own workers that do the same thing (aunque siguen the W3 standard, mientras que Google Gears usa sus propios métodos).

+1

Gracias. Esto funcionó muy bien para mí, pero estaba trabajando con una variedad de datos, por lo que podría no ser la mejor solución para todos. – Rob

+0

No funcionará para un control existente como una vista en árbol que no permite desglosar una tarea como actualizar el árbol, lo que implica iterar sobre cada nodo. – Triynko

1

Este es un ejemplo muy básico de cómo crear subprocesos en JavaScript. Tenga en cuenta que depende de usted interrumpir las funciones de subprocesos (instrucción yeld). Si lo desea, puede usar un setTimeout en lugar de mi , mientras que loop ejecuta el planificador periódicamente. Tenga en cuenta también que este ejemplo sólo funciona con la versión JavaScript 1.7+ (Firefox 3) puede probar aquí: http://jslibs.googlecode.com/svn/trunk/jseval.html

//// thread definition 
function Thread(name) { 

    for (var i = 0; i < 5; i++) { 

     Print(i+' ('+name+')'); 
     yield; 
    } 
} 

//// thread management 
var threads = []; 

// thread creation 
threads.push(new Thread('foo')); 
threads.push(new Thread('bar')); 

// scheduler 
while (threads.length) { 

    var thread = threads.shift(); 
    try { 
     thread.next(); 
     threads.push(thread); 
    } catch(ex if ex instanceof StopIteration) { } 
} 

La salida es:

0 (foo) 0 (bar) 1 (foo) 1 (bar) 2 (foo) 2 (bar) 3 (foo) 3 (bar) 4 (foo) 4 (bar) 
+0

Eso es genial aunque no factible a menos que el usuario se vea obligado a usar Firefox 3+. '=)' Me gusta mucho su evaluador de JavaScript, ¡lo marcaré! '=)' – Blixt

+0

¡Hola, gracias! Quizás pueda echar un vistazo al proyecto jslibs: http://code.google.com/p/jslibs –

8

Si se puede hacer cumplir el navegador para ser utilizado, o si ya sabes que es una nueva versión de Firefox, puedes usar el nuevo WebWorkers de Mozilla. Te permite generar nuevos hilos.

2

Gran respuesta Kevin! Escribí algo similar hace unos años, aunque menos sofisticado.El código fuente está aquí si alguien lo quiere:

http://www.leapbeyond.com/ric/jsUtils/TaskQueue.js

Cualquier cosa con un método run() puede poner en la cola como una tarea. Las tareas pueden volver a cola para realizar el trabajo en fragmentos. Puede priorizar tareas, agregarlas o eliminarlas a voluntad, pausar/reanudar toda la cola, etc. Funciona bien con operaciones asincrónicas. Mi uso original para esto fue administrar varias XMLHttpRequests concurrentes.

El uso básico es bastante simple:

var taskQueue = new TaskQueue(); 
taskQueue.schedule("alert('hello there')"); 

Los comentarios de cabecera en el archivo .js proporcionan ejemplos más avanzados.

0

Parece que este problema se ha resuelto en el nodo.
requieren child_process que se incluye en nodejs 0.10.4

2

Aquí está mi solución al problema, en caso de que alguien quiere pieza simple copia-pasteable de código:

var iterate = function (from, to, action, complete) { 
     var i = from; 
     var impl = function() { 
      action(i); 
      i++; 
      if (i < to) setTimeout(impl, 1); 
      else complete(); 
     }; 
     impl(); 
    }; 
Cuestiones relacionadas