¿Cómo puedo ejecutar múltiples scripts npm en paralelo?

543

En mi package.jsontengo estos dos scripts:

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

Tengo que ejecutar estos 2 scripts en paralelo cada vez que empiezo a desarrollar en Node.js. Lo primero que pensé fue agregar un tercer script como este:

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

... pero eso esperará a start-watchque termine antes de correr wp-server.

¿Cómo puedo ejecutar estos en paralelo? Tenga en cuenta que necesito ver outputestos comandos. Además, si su solución involucra una herramienta de compilación, prefiero usarla en gulplugar de gruntporque ya la uso en otro proyecto.

André Pena
fuente
23
&&ejecutará sus scripts secuencialmente mientras &los ejecutará en paralelo .
vsync
Una forma rápida de hacerlo es npm run start-watch & npm run wp-server. Esto ejecutará el primer comando como un hilo de fondo. Esto funciona realmente bien cuando uno de los comandos no se está ejecutando por mucho tiempo y no es necesario salir manualmente más tarde. Algo así le concurrentlypermite matar todos los hilos al mismo tiempo con CTRL-C.
Joshua Pinter el

Respuestas:

617

Use un paquete llamado simultáneamente .

npm i concurrently --save-dev

Luego configure su npm run devtarea de la siguiente manera:

"dev": "concurrently --kill-others \"npm run start-watch\" \"npm run wp-server\""
Neil Kistner
fuente
11
node ./node_modules/concurrently/src/main.jsno es necesario. concurrentfuncionará bien en los scripts porque el módulo instala un bin para./node_modules/.bin/concurrent
raine
14
También hay paralelshell . De hecho, recomiendo que uno concurrentlyuse múltiples transmisiones que se metan con la salida de la consola (los colores pueden ser extraños, el cursor desaparecido) mientras parallelshellque no tiene ese problema .
Stijn de Witt
3
Los errores mencionados simultáneamente por @StijndeWitt ahora se han corregido en la versión 2.0.0 . Puede usar el --rawmodo para preservar los colores en la salida.
Kimmo
23
@StijndeWitt parallelshell ha quedado en desuso a favor de npm-run-all github.com/keithamus/…
jtzero
12
Tiene que haber una mejor manera para que podamos administrar los scripts de compilación / ejecución de Javascript. Todo para esta plataforma parece estar unido. comillas con comillas escapadas y compilaciones npm para llamar a otras compilaciones 'npm run'. Esto se está volviendo bastante doloroso.
Andrew T Finnell
142

Si está utilizando un entorno similar a UNIX, simplemente utilícelo &como separador:

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

De lo contrario, si está interesado en una solución multiplataforma, puede usar el módulo npm-run-all :

"dev": "npm-run-all --parallel start-watch wp-server"
Diogo Cardoso
fuente
14
Hago esto, de vez en cuando cuando "ctrl-c" npm, el comando sigue colgado en segundo plano ... ¿Alguna idea?
Kamil Tomšík
13
a && bcomienza bdespués de afinalizar con éxito, pero nodemon nunca se detiene sin errores, por lo que eso no puede funcionar. a & bcomienza a, lo mueve al fondo y comienza de binmediato. ¡Ganar! a | bcanaliza la salida aestándar a la entrada estándar, lo bque requiere que ambos se ejecuten simultáneamente. Aunque parezca tener el efecto deseado, no debe usarlo aquí.
j2L4e
8
@ KamilTomšík &es una muy mala idea, ya que separa el proceso. Significa que npmya no será el proceso padre. Terminarás con un zombie con el npm run start-watchque no matarás ctrl-c.
ngryman
66
Solo agregue waitpara mitigar el problema con los procesos colgantes:"dev": "npm run start-watch & npm run wp-server & wait"
Ruslan Prokopchuk
2
No es un zombie. Pero &en unix evita que el comando responda a Cc / Cz y también evita que su código de retorno se propague en caso de falla.
binki
77

Desde Windows cmd puedes usar start:

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

Cada comando lanzado de esta manera comienza en su propia ventana.

ov
fuente
2
¡Solución perfecta! Me encanta que lance la nueva ventana. Ideal para las necesidades del paquete VS2015.json
TetraDev
13
Esto no funciona si tiene tareas de observador porque &&espera a que termine el primer comando antes de iniciar el segundo comando y una tarea de observador nunca finalizará.
Benny Neugebauer
2
@BennyNeugebauer Los comandos están precedidos por el comando "inicio" que abre una nueva línea de comando para cada uno de los comandos. Al principio también estaba confundido porque pensé que "usar el operador && no funcionará". Esta solución es muy simple y no requiere paquetes / trabajos adicionales del desarrollador.
Addison el
55
Esto está mal. El comando se ejecutará secuencialmente. En Windows, debe usar un complemento para ejecutar comandos simultáneamente.
zhekaus
1
¿No es esto específico de Windows?
binki
62

Debería usar npm-run-all (o concurrently, parallelshell), porque tiene más control sobre los comandos de inicio y finalización. Los operadores &,| son malas ideas, dado que tiene que parar manualmente después de que todas las pruebas se terminaron.

Este es un ejemplo para las pruebas de transportador a través de 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 = Ejecutar comandos en paralelo.

-r = Mata todos los comandos cuando uno de ellos termina con un código de salida de cero.

La ejecución npm run testiniciará el controlador Selenium, iniciará el servidor http (para servirle los archivos) y ejecutará pruebas de transportador. Una vez que todas las pruebas hayan finalizado, cerrará el servidor http y el controlador de selenio.

nir
fuente
3
Sin embargo, me pregunto cómo funciona esto correctamente para ejecutar las pruebas. Si bien webdriver-start y http-server pueden ejecutarse en paralelo, la tarea del transportador solo debería ejecutarse después de los dos primeros.
asenovm
@asenovm para tareas dependientes del pedido, ¿por qué no solo usar gulpy gulp-sync?
r3wt
30

Puede usar uno &para el script de ejecución paralela

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

Link de referencia

Behnam Mohammadi
fuente
¿Funcionará esto también en Windows? Lo siento, soy bastante nuevo en el nodo y no sé cómo verificar esto.
Benison Sam
@BenisonSam no, aunque funciona en Mac
shanehoban
25

Una mejor solución es usar &

"dev": "npm run start-watch & npm run wp-server"
Corey
fuente
54
No, no es mejor porque no funciona en todas las plataformas.
Stijn de Witt
No sabía eso. ¿En qué plataformas no funciona? @Corey: actualice su respuesta con la advertencia en el interoperatorio y lo votaré a usted
Ashley Coolman
8
&funciona en Windows, pero funciona de manera diferente. En OSX, ejecutará ambos comandos al mismo tiempo, pero en Windows, ejecutará el primer comando, y después de que exista el primer comando, ejecutará el segundo comando.
Trevor
3
No, no es porque separa el proceso, no podrás matarlo de una manera simple.
ngryman
2
@ngryman Eso es lo que yo también esperaba. Sin embargo, probé esto y mata los tres procesos (dev, start-watch y wp-server) cuando presionas Ctrl + C.
musicin3d
17

He comprobado casi todas las soluciones anteriores y solo con npm-run-all pude resolver todos los problemas. La principal ventaja sobre todas las demás soluciones es la capacidad de ejecutar secuencias de comandos con argumentos .

{
  "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 nota run-pes un atajo paranpm-run-all --parallel

Esto me permite ejecutar comandos con argumentos como npm run test:watch -- Something.

EDITAR:

Hay una opción más útil para 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.

Agregue -ra su npm-run-allscript para eliminar todos los procesos cuando uno termine con el código 0. Esto es especialmente útil cuando ejecuta un servidor HTTP y otro script que usa el servidor.

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

Tengo una solución de plataforma cruzada sin módulos adicionales . Estaba buscando algo como un bloque try catch que podría usar tanto en cmd.exe como en bash.

La solución es la command1 || command2que parece funcionar en ambos entornos. Entonces la solución para el OP es:

"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"
}

¡Entonces simple npm start(y npm run dev) funcionará en todas las plataformas!

Entidad negro
fuente
11

Si reemplaza el doble ampersand con un solo ampersand, los scripts se ejecutarán simultáneamente.

Neil Girardi
fuente
Exactamente, es simple y elegante, sin necesidad de dependencias u otra magia.
magikMaker
1
@Ginzburg Porque no funciona igual para todas las plataformas, como puedes ver en otras respuestas.
Jorge Fuentes González
6

Solución rápida

En este caso, diría que es la mejor opción si este script es para un módulo privado destinado a ejecutarse solo en máquinas basadas en * nix , puede usar el operador de control para los procesos de bifurcación, que se ve así:&

Un ejemplo de hacer esto en un archivo package.json parcial:

{
  "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 &"
  }

Luego los ejecutarías a ambos en paralelo a través de npm run serve-bundle. Puede mejorar los scripts para generar los pids del proceso bifurcado en un archivo de esta manera:

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

Google algo así como el operador de control bash para bifurcación para aprender más sobre cómo funciona. También proporcioné un contexto adicional sobre el aprovechamiento de las técnicas de Unix en los proyectos Node a continuación:

Contexto adicional RE: Unix Tools & Node.js

Si no está en Windows, las herramientas / técnicas de Unix a menudo funcionan bien para lograr algo con los scripts de Node porque:

  1. Gran parte de Node.js imita amorosamente los principios de Unix
  2. Estás en * nix (incluido OS X) y NPM está usando un shell de todos modos

Los módulos para tareas de sistema en Nodeland también son a menudo abstracciones o aproximaciones de herramientas Unix, desde fshasta streams.

james_womack
fuente
1
No, ya que el &operador no es compatible con Windows.
Stijn de Witt
3
@StijndeWitt mi publicación dice "Si no estás en Windows ...". 0% de las personas con las que trabajo, en una de las compañías tecnológicas más grandes del mundo, ejecutan Node en Windows. Claramente, mi publicación sigue siendo valiosa para muchos desarrolladores.
james_womack
2
Es una especie de forma circular de razonamiento, ¿no es así? Si escribe sus scripts npm como este, no podrá usar Windows porque no funcionará. Así que nadie usa Windows, por lo que no importa que no funcione ... Terminas con un software dependiente de la plataforma. Ahora, si lo que hay que hacer es muy difícil de hacer multiplataforma, eso podría ser una buena compensación. Pero este problema aquí es muy fácil de hacer con los scripts npm estándar, como concurrente y paralelo .
Stijn de Witt
2
@StijndeWitt Ninguno de mis razonamientos fue circular. Hice una declaración de hecho sin razonamiento. Estamos publicando técnicas comunes para los desarrolladores de Node, muchos de los cuales compilan e implementan en servidores Linux. Sí, debería funcionar en Windows si es un script de userland, pero la mayoría de los scripts npm son para desarrollo e implementación, principalmente en máquinas * nix. Con respecto a los módulos que mencionó a) es muy difícil llamar simultáneamente y "estándar" en paralelo (~ 1500 descargas por día está lejos de ser estándar en NPMland) yb) si necesita software adicional para un proceso paralelo, también podría usar Trago.
james_womack
@StijndeWitt Sin embargo, agradezco que me hayan dado cuenta de esos módulos, gracias
James_womack el
6
npm-run-all --parallel task1 task2

editar:

Debe tener npm-run-all instalado de antemano. Consulte también esta página para ver otros escenarios de uso.

noego
fuente
5

¿Qué hay de bifurcación

Otra opción para ejecutar múltiples scripts de Nodo es con un solo script de Nodo, que puede bifurcar muchos otros. La bifurcación se admite de forma nativa en Node, por lo que no agrega dependencias y es multiplataforma.


Ejemplo mínimo

Esto solo ejecutaría los scripts tal cual y supondría que están ubicados en el directorio del script principal.

// 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));

Ejemplo detallado

Esto ejecutaría los scripts con argumentos y configurados por las muchas opciones 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
});

Comunicación con guiones bifurcados

Forking también tiene el beneficio adicional de que el script primario puede recibir eventos de los procesos secundarios bifurcados, así como también enviarlos de regreso. Un ejemplo común es que el script padre mate a sus hijos bifurcados.

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

Para más eventos y métodos disponibles, consulte la ChildProcessdocumentación

Boaz - Restablece a Monica
fuente
3

Me encontré con problemas &y| , que salen de los estados y arrojan errores, respectivamente.

Otras soluciones quieren ejecutar cualquier tarea con un nombre de pila, como npm-run-all, que no era mi caso de uso.

Así que creé npm-run-parallel que ejecuta scripts npm de forma asincrónica e informa cuando están listos.

Entonces, para sus guiones, sería:

npm-run-parallel wp-server start-watch

ian
fuente
2

En mi caso tengo dos proyectos, uno era UI y el otro era API , y ambos tienen su propio script en sus respectivospackage.json archivos.

Entonces, esto es lo que hice.

npm run --prefix react start&  npm run --prefix express start&
Vikash Mishra
fuente
Me gusta tu solución. También tiene UI ( node app) y API (Angular en una subcarpeta src , supongo cd src/ng serve), solo funciona la primera parte. Por ejemplo node app& cd src& ng serve.
Jeb50
1

He estado usando npm-run-all durante algún tiempo, pero nunca me llevé bien con él, porque la salida del comando en modo reloj no funciona bien juntos. Por ejemplo, si empiezo create-react-appyjest en modo de observación, solo podré ver el resultado del último comando que ejecuté. Así que la mayoría de las veces, estaba ejecutando todos mis comandos manualmente ...

Por eso, implemento mi propia lib, run-screen . Todavía es un proyecto muy joven (de ayer: p) pero podría valer la pena mirarlo, en su caso sería:

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

Luego presiona la tecla numérica 1para ver la salida de wp-servery presiona 0para ver la salida de start-watch.

Alexandre
fuente
1

Mi solución es similar a la de Piittis, aunque tuve algunos problemas al usar Windows. Entonces tuve que validar para 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");
})();

fuente
0

Script de nodo simple para que pueda comenzar sin demasiados problemas. Usando readline para combinar salidas para que las líneas no se rompan.

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);
});
Piittis
fuente
0
"dev": "(cd api && start npm run start) & (cd ../client && start npm run start)"

este trabajo en windows

SB3NDER
fuente