54 votes

Exécuter une tâche d'arrière-plan en Javascript

J'ai une tâche à forte intensité de processeur que je dois exécuter sur le client. Idéalement, j'aimerais pouvoir invoquer la fonction et déclencher des événements de progression à l'aide de jquery afin de pouvoir mettre à jour l'interface utilisateur.

Je sais que javascript ne supporte pas le threading, mais j'ai vu quelques articles prometteurs essayant d'imiter le threading en utilisant setTimeout.

Quelle est la meilleure approche à utiliser pour cela ? Merci.

40voto

Blixt Points 23266

En gros, ce que vous voulez faire, c'est diviser l'opération en morceaux. Supposons que vous ayez 10 000 éléments à traiter, stockez-les dans une liste, puis traitez un petit nombre d'entre eux avec un petit délai entre chaque appel. Voici une structure simple que vous pourriez utiliser :

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

Notez également que Google Gears permet d'effectuer des travaux sur un fil distinct. . Firefox 3.5 a également introduit ses propres travailleurs qui font la même chose (bien qu'ils suivent la norme W3 tandis que Google Gears utilise ses propres méthodes).

1 votes

Merci. Cela a très bien fonctionné pour moi, mais je travaillais avec un tableau de données, donc ce n'est peut-être pas la meilleure solution pour tout le monde.

0 votes

Cela ne fonctionnera pas pour un contrôle existant tel qu'une arborescence qui ne permet pas de décomposer une tâche telle que la mise à jour de l'arbre, qui implique d'itérer sur chaque nœud.

20voto

Kevin Points 136

J'ai eu un problème similaire à résoudre récemment, où j'avais besoin de garder le thread de mon interface utilisateur libre pendant le traitement de certaines données à afficher.

J'ai écrit une bibliothèque Background.js pour gérer quelques scénarios : une file d'attente séquentielle en arrière-plan (basée sur la bibliothèque WorkerQueue), une liste de tâches où chacune est appelée à chaque timer, et un itérateur de tableau pour aider à diviser votre travail en petits morceaux. Exemples et code ici : https://github.com/kmalakoff/background

Profitez-en !

10voto

Sinan Taifour Points 3878

Si vous pouvez imposer le navigateur à utiliser, ou si vous savez déjà qu'il s'agit d'une nouvelle version de Firefox, vous pouvez utiliser la nouvelle option WebWorkers de Mozilla. Il vous permet de créer de nouveaux fils de discussion.

4voto

Jani Hartikainen Points 23183

En fonction de vos besoins, vous pouvez vous en sortir facilement en utilisant Gears. Gears prend en charge les threads, qui pourraient faire ce que vous voulez.

Comme vous l'avez mentionné, setTimeout est l'autre option. Selon le type de votre tâche, vous pouvez confier chaque itération d'une boucle à un appel setTimeout distinct, avec un certain espacement entre les deux, ou vous pouvez avoir besoin de séparer des parties de votre algorithme principal en fonctions distinctes qui peuvent être appelées une par une de la même manière que vous appelleriez chaque itération.

0 votes

+1, mais les "workers" ne sont pas exactement ce que d'autres environnements appellent des "threads".

0 votes

Firefox 3.5 dispose également de travailleurs standardisés W3. Voir ma réponse pour les liens.

3voto

rkagerer Points 1700

Excellente réponse, Kevin ! J'ai écrit quelque chose de similaire il y a quelques années, mais moins sophistiqué. Le code source est ici si quelqu'un le veut :

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

Tout ce qui a un run() peut être mise en file d'attente en tant que tâche. Les tâches peuvent être remises en file d'attente afin d'effectuer le travail par morceaux. Vous pouvez classer les tâches par ordre de priorité, les ajouter ou les supprimer à volonté, mettre en pause ou reprendre toute la file d'attente, etc. Fonctionne bien avec les opérations asynchrones - mon utilisation initiale était de gérer plusieurs XMLHttpRequests simultanés.

L'utilisation de base est assez simple :

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

Les commentaires d'en-tête du fichier .js fournissent des exemples plus avancés.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X