79 votes

Coordonner l’exécution parallèle de node.js

La programmation événementielle modèle de node.js le rend un peu difficile à coordonner le déroulement du programme.

Simple exécution séquentielle est transformé en imbriqués les rappels, ce qui est assez facile (bien qu'un peu compliqué à écrire).

Mais comment au sujet de l'exécution en parallèle? Disons que vous avez trois tâches A,B,C qui peuvent s'exécuter en parallèle, et quand elles sont faites, vous voulez envoyer leurs résultats à la tâche D.

Avec un fork/join modèle ce serait

  • fourche
  • fourche B
  • fourche C
  • joindre A,B,C, D exécuter

Comment puis-je écrire que dans node.js ? Existe-il des pratiques exemplaires ou des livres de cuisine? Dois-je la main-roll une solution à chaque fois, ou est-il une bibliothèque avec des aides pour cela?

129voto

slebetman Points 28276

Rien n'est véritablement parallèle dans node.js depuis qu'il est mono-thread. Cependant, plusieurs événements peuvent être planifié et exécuté dans une séquence que vous ne pouvez pas déterminer à l'avance. Et certaines choses comme l'accès de base de données sont en fait des "parallèle" dans la base de données requêtes sont exécutées dans des threads séparés, mais sont ré-intégré dans le flux d'événements.

Alors, comment voulez-vous programmer un rappel sur plusieurs gestionnaires d'événements? Eh bien, c'est une technique courante utilisée dans les animations dans le navigateur javascript: utilisation d'une variable pour suivre la réalisation.

Cela sonne comme un hack et il est, et il semble potentiellement salissante, laissant un tas de variables globales autour de faire le suivi et, dans une moindre langue il serait. Mais en javascript, nous pouvons utiliser des bouchons:

function fork (async_calls, shared_callback) {
  var counter = async_calls.length;
  var callback = function () {
    counter --;
    if (counter == 0) {
      shared_callback()
    }
  }

  for (var i=0;i<async_calls.length;i++) {
    async_calls[i](callback);
  }
}

// usage:
fork([A,B,C],D);

Dans l'exemple ci-dessus, nous simplifier le code en supposant la async et les fonctions de rappel nécessitent pas d'arguments. Vous pouvez bien sûr modifier le code pour passer des arguments à la async fonctions et ont la fonction de rappel d'accumuler les résultats et le passer à la shared_callback fonction.


Réponse supplémentaire:

En fait, même comme il est, qu' fork() fonction peut déjà passer des arguments à la async fonctions à l'aide d'une fermeture:

fork([
  function(callback){ A(1,2,callback) },
  function(callback){ B(1,callback) },
  function(callback){ C(1,2,callback) }
],D);

la seule chose qui reste à faire est d'accumuler les résultats à partir de A,B,C et D.


Même plus de réponse:

Je ne pouvais pas résister. N'arrêtais pas de penser à ce sujet pendant le petit déjeuner. Voici une implémentation de l' fork() qui accumule les résultats (généralement passés en argument à la fonction de rappel):

function fork (async_calls, shared_callback) {
  var counter = async_calls.length;
  var all_results = [];
  function makeCallback (index) {
    return function () {
      counter --;
      var results = [];
      // we use the arguments object here because some callbacks 
      // in Node pass in multiple arguments as result.
      for (var i=0;i<arguments.length;i++) {
        results.push(arguments[i]);
      }
      all_results[index] = results;
      if (counter == 0) {
        shared_callback(all_results);
      }
    }
  }

  for (var i=0;i<async_calls.length;i++) {
    async_calls[i](makeCallback(i));
  }
}

C'était assez facile. Cela rend fork() assez d'usage général et peut être utilisé pour synchroniser plusieurs non-homogène des événements.

Exemple d'utilisation dans Node.js:

// Read 3 files in parallel and process them together:

function A (c){ fs.readFile('file1',c) };
function B (c){ fs.readFile('file2',c) };
function C (c){ fs.readFile('file3',c) };
function D (result) {
  file1data = result[0][1];
  file2data = result[1][1];
  file3data = result[2][1];

  // process the files together here
}

fork([A,B,C],D);

Mise à jour

Ce code a été écrit avant l'existence de bibliothèques, comme async.js ou les différents promesse basée bibliothèques. J'aimerais croire que async.js a été inspiré par cela, mais je n'ai pas de preuve. De toute façon.. si vous envisagez de le faire aujourd'hui, jetez un oeil à async.js ou promesses. Il suffit de considérer la réponse ci-dessus une bonne explication/illustration de la façon dont les choses comme async.en parallèle, un travail.

10voto

Wes Gamble Points 412

Je crois que maintenant le module « async » fournit cette fonctionnalité parallèle et est à peu près identique à la fonction de fourche ci-dessus.

5voto

Zoramite Points 308

Le terme module est un sous-module appelé rejoindre que j'ai bien aimé utiliser:

Les jointures des appels asynchrones ensemble similaire à la façon dont pthread_join fonctionne pour les threads.

Le fichier readme montre quelques bons exemples de l'utilisation de ce freestyle ou à l'aide de la future sous-module à l'aide de la Promesse de modèle. Exemple de la doc:

var Join = require('join')
  , join = Join()
  , callbackA = join.add()
  , callbackB = join.add()
  , callbackC = join.add();

function abcComplete(aArgs, bArgs, cArgs) {
  console.log(aArgs[1] + bArgs[1] + cArgs[1]);
}

setTimeout(function () {
  callbackA(null, 'Hello');
}, 300);

setTimeout(function () {
  callbackB(null, 'World');
}, 500);

setTimeout(function () {
  callbackC(null, '!');
}, 400);

// this must be called after all 
join.when(abcComplete);

2voto

Wilhelm Murdoch Points 860

Une autre option pourrait être le module d’étape pour noeud : https://github.com/creationix/step

2voto

Alex Epifano Points 860

Une solution simple serait possible ici : http://howtonode.org/control-flow-part-ii faites défiler jusqu'à actions parallèles. Une autre façon serait d’avoir des A, B et C partagent tous la même fonction de rappel, que fonction ont un global ou du moins hors-de-la-fonction incrementor, si tous les trois ont appelé le rappel puis laissez-le courir D, bien sûr, vous devez stocker les résultats d’un , B et C quelque part aussi bien.

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