Comment exécuter plusieurs scripts npm en parallèle?


543

Dans mon package.jsonj'ai ces deux scripts:

  "scripts": {
    "start-watch": "nodemon run-babel index.js",
    "wp-server": "webpack-dev-server",
  }

Je dois exécuter ces 2 scripts en parallèle chaque fois que je commence à développer dans Node.js. La première chose à laquelle j'ai pensé était d'ajouter un troisième script comme celui-ci:

"dev": "npm run start-watch && npm run wp-server"

... mais il faudra attendre pour start-watchterminer avant de courir wp-server.

Comment puis-je les exécuter en parallèle? Veuillez garder à l'esprit que j'ai besoin de voir la outputde ces commandes. De plus, si votre solution implique un outil de construction, je préfère utiliser gulpplutôt que gruntparce que je l'utilise déjà dans un autre projet.


23
&&exécutera vos scripts séquentiellement tout &en les exécutant en parallèle .
vsync

Un moyen rapide de le faire est npm run start-watch & npm run wp-server. Cela exécutera la première commande en tant que thread d'arrière-plan. Cela fonctionne très bien lorsque l'une des commandes n'est pas longue et n'a pas besoin d'être fermée manuellement plus tard. Quelque chose comme concurrentlyvous permet de tuer tous les threads en même temps avec CTRL-C.
Joshua Pinter

Réponses:


617

Utilisez un package appelé simultanément .

npm i concurrently --save-dev

Ensuite, configurez votre npm run devtâche comme suit:

"dev": "concurrently --kill-others \"npm run start-watch\" \"npm run wp-server\""

11
node ./node_modules/concurrently/src/main.jsn'est pas nécessaire. concurrentfonctionnera très bien dans les scripts parce que le module installe un bac pour./node_modules/.bin/concurrent
raine

14
Il y a aussi le parallèle . Je recommande en fait que l'un concurrentlyutilise plusieurs flux qui gâchent la sortie de la console (les couleurs peuvent devenir étranges, le curseur disparu) alors qu'il parallelshelln'a pas ce problème .
Stijn de Witt

3
Les bogues mentionnés simultanément par @StijndeWitt ont maintenant été corrigés dans la version 2.0.0 . Vous pouvez utiliser le --rawmode pour conserver les couleurs en sortie.
Kimmo

23
@StijndeWitt parallelshell a été déconseillé au profit de npm-run-all github.com/keithamus/…
jtzero

12
Il doit y avoir un meilleur moyen pour nous de gérer les scripts de génération / exécution de Javascript. Tout pour cette plateforme semble cloué ensemble. des guillemets avec des guillemets échappés et des builds npm pour appeler d'autres builds 'npm run'. Cela devient assez douloureux.
Andrew T Finnell

142

Si vous utilisez un environnement de type UNIX, utilisez-le simplement &comme séparateur:

"dev": "npm run start-watch & npm run wp-server"

Sinon, si vous êtes intéressé par une solution multiplateforme, vous pouvez utiliser le module npm-run-all :

"dev": "npm-run-all --parallel start-watch wp-server"

14
Je le fais - de temps en temps quand je "ctrl-c" npm, la commande continue de s'accrocher en arrière-plan ... Des idées?
Kamil Tomšík

13
a && bdémarre baprès avoir aterminé avec succès, mais nodemon ne s'arrête jamais sans erreurs, donc cela ne peut pas fonctionner. a & bdémarre a, le place en arrière-plan et démarre bimmédiatement. Gagner! a | bredirige la sortie astandard vers la sortie standard bqui nécessite que les deux s'exécutent simultanément. Bien que cela puisse sembler avoir l'effet souhaité, vous ne devriez pas l'utiliser ici.
j2L4e

8
@ KamilTomšík &est une très mauvaise idée car elle détache le processus. Cela signifie que npmce ne sera plus le processus parent. Vous vous retrouverez avec un zombie npm run start-watchqui ne sera pas tué avec ctrl-c.
ngryman

6
Ajoutez simplement waitpour atténuer le problème avec les processus de suspension:"dev": "npm run start-watch & npm run wp-server & wait"
Ruslan Prokopchuk

2
Ce n'est pas un zombie. Mais &sous unix empêche la commande de répondre à Cc / Cz et empêche également son code de retour de se propager en cas d'échec.
binki

77

Depuis Windows cmd, vous pouvez utiliser start:

"dev": "start npm run start-watch && start npm run wp-server"

Chaque commande lancée de cette façon démarre dans sa propre fenêtre.


2
Solution parfaite! J'adore qu'il ouvre la nouvelle fenêtre. Idéal pour les besoins package.json de VS2015
TetraDev

13
Cela ne fonctionne pas si vous avez des tâches d'observation car &&attend la fin de la première commande avant de démarrer la deuxième commande et une tâche d'observation ne se terminera jamais.
Benny Neugebauer

2
@BennyNeugebauer Les commandes sont précédées de la commande "start" qui ouvre une nouvelle ligne de commande pour chacune des commandes. J'étais aussi confus au début parce que je pensais que "l'utilisation de l'opérateur && ne fonctionnera pas". Cette solution est très simple et ne nécessite aucun package / travail supplémentaire de la part du développeur.
Addison

5
C'est faux. La commande sera exécutée séquentiellement. Sous Windows, vous devez utiliser un plugin pour exécuter des commandes simultanément.
zhekaus

1
N'est-ce pas spécifique à Windows?
binki

62

Vous devez utiliser npm-run-all (ou concurrently, parallelshell), car il a plus de contrôle sur le démarrage et la suppression des commandes. Les opérateurs &,| sont de mauvaises idées parce que vous aurez besoin d'arrêter manuellement après tous les tests sont terminés.

Voici un exemple de test de rapporteur via npm:

scripts: {
  "webdriver-start": "./node_modules/protractor/bin/webdriver-manager update && ./node_modules/protractor/bin/webdriver-manager start",
  "protractor": "./node_modules/protractor/bin/protractor ./tests/protractor.conf.js",
  "http-server": "./node_modules/http-server/bin/http-server -a localhost -p 8000",
  "test": "npm-run-all -p -r webdriver-start http-server protractor"
}

-p = Exécuter des commandes en parallèle.

-r = Tuez toutes les commandes lorsque l'une d'elles se termine par un code de sortie nul.

L'exécution npm run testdémarrera le pilote Selenium, démarrera le serveur http (pour vous servir des fichiers) et exécutera des tests de rapporteur. Une fois tous les tests terminés, il fermera le serveur http et le pilote sélénium.


3
Je me demande cependant comment cela fonctionne correctement pour exécuter les tests. Alors que webdriver-start et http-server peuvent s'exécuter en parallèle, la tâche du rapporteur ne doit s'exécuter qu'après les deux premiers.
asenovm

@asenovm pour les tâches dépendantes de la commande, pourquoi ne pas simplement utiliser gulpet gulp-sync?
r3wt

30

Vous pouvez en utiliser un &pour le script d'exécution parallèle

"dev": "npm run start-watch & npm run wp-server"

Lien de référence


Cela fonctionnera-t-il également sous Windows? Désolé, je suis assez nouveau sur le nœud et je ne sais pas comment le vérifier!
Benison Sam

@BenisonSam non, fonctionne sur Mac
shanehoban

25

Une meilleure solution consiste à utiliser &

"dev": "npm run start-watch & npm run wp-server"

54
Non, ce n'est pas mieux car cela ne fonctionne pas sur toutes les plateformes.
Stijn de Witt

Je ne le savais pas. Sur quelles plateformes cela ne fonctionne-t-il pas? @Corey - mettez à jour votre réponse avec l'avertissement sur l'interopérabilité et je vous voterai positivement
Ashley Coolman

8
&fonctionne sur Windows, mais cela fonctionne différemment. Sous OSX, il exécutera les deux commandes simultanément, mais sous Windows, il exécutera la première commande et une fois la première commande existante, il exécutera la deuxième commande.
Trevor

3
Non, il ne détache pas le processus, vous ne pourrez pas le tuer de manière simple.
ngryman

2
@ngryman C'est ce que j'attendais aussi. Cependant, j'ai essayé cela et cela tue les trois processus (dev, start-watch et wp-server) lorsque vous appuyez sur Ctrl + C.
musicin3d

17

J'ai vérifié presque toutes les solutions ci-dessus et seulement avec npm-run-all j'ai pu résoudre tous les problèmes. Le principal avantage sur toutes les autres solutions est la possibilité d' exécuter un script avec des arguments .

{
  "test:static-server": "cross-env NODE_ENV=test node server/testsServer.js",
  "test:jest": "cross-env NODE_ENV=test jest",
  "test": "run-p test:static-server \"test:jest -- {*}\" --",
  "test:coverage": "npm run test -- --coverage",
  "test:watch": "npm run test -- --watchAll",
}

La note run-pest un raccourci pournpm-run-all --parallel

Cela me permet d'exécuter la commande avec des arguments comme npm run test:watch -- Something.

ÉDITER:

Il y en a un de plus utile option pour npm-run-all:

 -r, --race   - - - - - - - Set the flag to kill all tasks when a task
                            finished with zero. This option is valid only
                            with 'parallel' option.

Ajoutez -rà votre npm-run-allscript pour tuer tous les processus lorsque l'un a terminé avec le code 0. Cela est particulièrement utile lorsque vous exécutez un serveur HTTP et un autre script utilisant le serveur.

  "test": "run-p -r test:static-server \"test:jest -- {*}\" --",

15

J'ai une solution multiplateforme sans modules supplémentaires . Je cherchais quelque chose comme un bloc try catch que je pourrais utiliser à la fois dans le cmd.exe et dans le bash.

La solution est celle command1 || command2qui semble fonctionner de la même manière dans les deux environnements. La solution pour l'OP est donc:

"scripts": {
  "start-watch": "nodemon run-babel index.js",
  "wp-server": "webpack-dev-server",
  // first command is for the cmd.exe, second one is for the bash
  "dev": "(start npm run start-watch && start npm run wp-server) || (npm run start-watch & npm run wp-server)",
  "start": "npm run dev"
}

Alors simple npm start(et npm run dev) fonctionnera sur toutes les plateformes!


11

Si vous remplacez la double esperluette par une seule esperluette, les scripts s'exécuteront simultanément.


Exactement, c'est simple et élégant, pas besoin de dépendances ou d'autre magie.
magikMaker

1
@Ginzburg Parce que ne fonctionne pas de la même manière pour toutes les plateformes, comme vous pouvez le voir dans d'autres réponses.
Jorge Fuentes González

6

Solution rapide

Dans ce cas, je dirais que le meilleur pari Si ce script est pour un module privé destiné à fonctionner uniquement sur des machines basées sur * nix , vous pouvez utiliser l'opérateur de contrôle pour les processus de forking, qui ressemble à ceci:&

Un exemple de cette opération dans un fichier package.json partiel:

{
  "name": "npm-scripts-forking-example",
  "scripts": {
    "bundle": "watchify -vd -p browserify-hmr index.js -o bundle.js",
    "serve":  "http-server -c 1 -a localhost",
    "serve-bundle": "npm run bundle & npm run serve &"
  }

Vous les exécuteriez ensuite tous les deux en parallèle via npm run serve-bundle. Vous pouvez améliorer les scripts pour sortir les pids du processus forké dans un fichier comme ceci:

"serve-bundle": "npm run bundle & echo \"$!\" > build/bundle.pid && npm run serve & echo \"$!\" > build/serve.pid && npm run open-browser",

Google quelque chose comme opérateur de contrôle bash pour la fourche pour en savoir plus sur son fonctionnement. J'ai également fourni un contexte supplémentaire concernant l'utilisation des techniques Unix dans les projets Node ci-dessous:

Contexte supplémentaire RE: Unix Tools & Node.js

Si vous n'êtes pas sous Windows, les outils / techniques Unix fonctionnent souvent bien pour réaliser quelque chose avec les scripts Node car:

  1. Une grande partie de Node.js imite avec amour les principes Unix
  2. Vous êtes sur * nix (incl. OS X) et NPM utilise quand même un shell

Les modules pour les tâches système dans Nodeland sont également souvent des abstractions ou des approximations des outils Unix, de fsà streams.


1
Non, car l' &opérateur n'est pas pris en charge sous Windows.
Stijn de Witt

3
@StijndeWitt mon message dit "Si vous n'êtes pas sous Windows ...". 0% des gens avec qui je travaille, dans l'une des plus grandes entreprises technologiques au monde, exécutent Node sur Windows. Il est donc clair que mon article est toujours précieux pour de nombreux développeurs.
james_womack

2
C'est une sorte de raisonnement circulaire, n'est-ce pas? Si vous écrivez vos scripts npm comme celui-ci, vous ne pourrez pas utiliser Windows car cela ne fonctionnera pas. Donc, personne n'utilise Windows, donc peu importe que cela ne fonctionne pas ... Vous vous retrouvez avec un logiciel dépendant de la plate-forme. Maintenant, si la chose à faire est très difficile à faire multiplateforme, cela pourrait être un bon compromis à faire. Mais ce problème ici est très facile à faire avec les scripts npm standard tels que simultanément et parallelshell .
Stijn de Witt

2
@StijndeWitt Aucun de mes raisonnements n'était circulaire. J'ai fait une déclaration de fait sans raisonnement. Nous publions des techniques communes aux développeurs Node, dont beaucoup construisent et déploient sur des serveurs Linux. Oui, il devrait fonctionner sur Windows s'il s'agit d'un script utilisateur, mais la majorité des scripts npm sont destinés au développement et au déploiement, principalement sur des machines * nix. En ce qui concerne les modules que vous avez mentionnés a) c'est un énorme tronçon d'appeler simultanément et parallelshell "standard" (~ 1500 téléchargements par jour est loin d'être standard dans NPMland) et b) si vous avez besoin d'un logiciel supplémentaire pour un processus parallèle, vous pourriez aussi bien utiliser Gorgée.
james_womack

@StijndeWitt J'apprécie cependant d'être mis au courant de ces modules - merci
james_womack

6
npm-run-all --parallel task1 task2

Éditer:

Vous devez avoir préalablement installé npm-run-all . Consultez également cette page pour d'autres scénarios d'utilisation.


5

Que diriez-vous de bifurquer

Une autre option pour exécuter plusieurs scripts Node consiste à utiliser un seul script Node, qui peut en générer de nombreux autres. Le fork est pris en charge nativement dans Node, il n'ajoute donc aucune dépendance et est multiplateforme.


Exemple minimal

Cela exécuterait simplement les scripts tels quels et supposerait qu'ils se trouvent dans le répertoire du script parent.

// fork-minimal.js - run with: node fork-minimal.js

const childProcess = require('child_process');

let scripts = ['some-script.js', 'some-other-script.js'];
scripts.forEach(script => childProcess.fork(script));

Exemple détaillé

Cela exécuterait les scripts avec des arguments et configurés par les nombreuses options disponibles.

// fork-verbose.js - run with: node fork-verbose.js

const childProcess = require('child_process');

let scripts = [
    {
        path: 'some-script.js',
        args: ['-some_arg', '/some_other_arg'],
        options: {cwd: './', env: {NODE_ENV: 'development'}}
    },    
    {
        path: 'some-other-script.js',
        args: ['-another_arg', '/yet_other_arg'],
        options: {cwd: '/some/where/else', env: {NODE_ENV: 'development'}}
    }
];

let processes = [];

scripts.forEach(script => {
    let runningScript = childProcess.fork(script.path, script.args, script.options);

   // Optionally attach event listeners to the script
   runningScript.on('close', () => console.log('Time to die...'))

    runningScripts.push(runningScript); // Keep a reference to the script for later use
});

Communiquer avec des scripts fourchus

Forking a également l'avantage supplémentaire que le script parent peut recevoir des événements des processus enfants forkés ainsi que les renvoyer. Un exemple courant est que le script parent tue ses enfants fourchus.

 runningScripts.forEach(runningScript => runningScript.kill());

Pour plus d'événements et de méthodes disponibles, consultez la ChildProcessdocumentation


3

J'ai rencontré des problèmes avec &et| , quels états de sortie et lancement d'erreur, respectivement.

D'autres solutions veulent exécuter n'importe quelle tâche avec un nom donné, comme npm-run-all, ce qui n'était pas mon cas d'utilisation.

J'ai donc créé npm-run-parallel qui exécute les scripts npm de manière asynchrone et rend compte quand ils ont terminé.

Donc, pour vos scripts, ce serait:

npm-run-parallel wp-server start-watch


2

Dans mon cas, j'ai deux projets, l'un était l' interface utilisateur et l'autre API , et les deux ont leur propre script dans leurs package.jsonfichiers respectifs .

Alors, voici ce que j'ai fait.

npm run --prefix react start&  npm run --prefix express start&

Comme votre solution. Ont également UI ( node app) et API (Angular dans un sous-dossier src , je suppose cd src/ng serve), seule la première partie fonctionne. Par exemple node app& cd src& ng serve.
Jeb50


1

J'utilise npm-run-all depuis un certain temps, mais je ne m'en suis jamais entendu, car la sortie de la commande en mode veille ne fonctionne pas bien ensemble. Par exemple, si je commence create-react-appetjest en mode veille, je ne pourrai voir que la sortie de la dernière commande que j'ai exécutée. Donc la plupart du temps, j'exécutais toutes mes commandes manuellement ...

C'est pourquoi, j'implémente ma propre lib, run-screen . C'est encore un projet très jeune (d'hier: p) mais ça vaut peut-être le coup d'oeil, dans votre cas ce serait:

run-screen "npm run start-watch" "npm run wp-server"

Ensuite, vous appuyez sur la touche numérique 1pour voir la sortie de wp-serveret appuyez sur 0pour voir la sortie de start-watch.


1

Ma solution est similaire à celle de Piittis, mais j'ai eu quelques problèmes avec Windows. J'ai donc dû valider pour win32.

const { spawn } = require("child_process");

function logData(data) {
    console.info(`stdout: ${data}`);
}

function runProcess(target) {
    let command = "npm";
    if (process.platform === "win32") {
        command = "npm.cmd"; // I shit you not
    }
    const myProcess = spawn(command, ["run", target]); // npm run server

    myProcess.stdout.on("data", logData);
    myProcess.stderr.on("data", logData);
}

(() => {
    runProcess("server"); // package json script
    runProcess("client");
})();

0

Script de nœud simple pour vous permettre de démarrer sans trop de tracas. Utiliser readline pour combiner les sorties afin que les lignes ne soient pas altérées.

const { spawn } = require('child_process');
const readline = require('readline');

[
  spawn('npm', ['run', 'start-watch']),
  spawn('npm', ['run', 'wp-server'])
].forEach(child => {
    readline.createInterface({
        input: child.stdout
    }).on('line', console.log);

    readline.createInterface({
        input: child.stderr,
    }).on('line', console.log);
});

0
"dev": "(cd api && start npm run start) & (cd ../client && start npm run start)"

ce travail dans les fenêtres

En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.