Como posso executar vários scripts npm em paralelo?

542

No meu package.jsoneu tenho esses dois scripts:

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

Eu tenho que executar esses 2 scripts em paralelo toda vez que começo a desenvolver no Node.js. A primeira coisa que pensei foi adicionar um terceiro script como este:

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

... mas isso vai esperar para start-watchterminar antes de executar wp-server.

Como posso executá-los em paralelo? Por favor, lembre-se de que preciso ver os outputcomandos. Além disso, se sua solução envolve uma ferramenta de compilação, prefiro usá-la em gulpvez de gruntusá-la em outro projeto.

André Pena
fonte
23
&&executará seus scripts sequencialmente enquanto &os executará em paralelo .
vsync
Uma maneira rápida de fazer isso é npm run start-watch & npm run wp-server. Isso executará o primeiro comando como um encadeamento em segundo plano. Isso funciona muito bem quando um dos comandos não é demorado e não precisa ser encerrado manualmente mais tarde. Algo como concurrentlypermite que você mate todos os threads ao mesmo tempo com CTRL-C.
Joshua Pinter

Respostas:

616

Use um pacote chamado simultaneamente .

npm i concurrently --save-dev

Em seguida, configure sua npm run devtarefa da seguinte maneira:

"dev": "concurrently --kill-others \"npm run start-watch\" \"npm run wp-server\""
Neil Kistner
fonte
11
node ./node_modules/concurrently/src/main.jsNão é necessário. concurrentvai funcionar muito bem em scripts porque o módulo instala um bin para./node_modules/.bin/concurrent
Raine
14
Também há paralelo . Na verdade, eu recomendo que alguém concurrentlyuse vários fluxos que mexam com a saída do console (as cores podem ficar estranhas, o cursor desapareceu) enquanto parallelshellnão tiver esse problema .
Stijn de Witt
3
Os erros mencionados simultaneamente pelo @StijndeWitt foram corrigidos na versão 2.0.0 . Você pode usar o --rawmodo para preservar as cores na saída.
Kimmo
23
O parallelshell do @StijndeWitt foi preterido em favor do npm-run-all github.com/keithamus/…
jtzero
12
Deve haver uma maneira melhor de gerenciar scripts de criação / execução de Javascript. Tudo para esta plataforma parece reunido. aspas com aspas escapadas e compilações npm para chamar outras compilações 'npm run'. Isso está ficando muito doloroso.
Andrew T Finnell
141

Se você estiver usando um ambiente semelhante ao UNIX, use &como separador:

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

Caso contrário, se você estiver interessado em uma solução de plataforma cruzada, poderá usar o módulo npm-run-all :

"dev": "npm-run-all --parallel start-watch wp-server"
Diogo Cardoso
fonte
14
Eu faço isso - de tempos em tempos, quando eu "ctrl-c" npm, o comando continua em segundo plano ... Alguma idéia?
Kamil Tomšík 15/03
13
a && binicia bapós a aconclusão com êxito, mas o nodemon nunca para sem erros, portanto isso não funciona. a & binicia a, move-o para o fundo e inicia bimediatamente. Ganhar! a | bcanaliza o stdout de apara o stdin do bqual requer ambos executando simultaneamente. Embora isso pareça ter o efeito desejado, você não deve usá-lo aqui.
j2L4e
8
@ KamilTomšík &é uma péssima idéia, pois desanexa o processo. Isso significa que npmnão será mais o processo pai. Você vai acabar com um zumbi npm run start-watchque não será morto ctrl-c.
Nckman 31/12/16
6
Basta adicionar waitao problema mitigar com os processos de suspensão:"dev": "npm run start-watch & npm run wp-server & wait"
Ruslan Prokopchuk
2
Não é um zumbi. Mas &no unix, impede que o comando responda a Cc / Cz e também impede que seu código de retorno se propague em caso de falha.
binki
77

No windows cmd, você pode usar start:

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

Todo comando lançado dessa maneira começa em sua própria janela.

ov
fonte
2
Solução perfeita! Eu amo que ele lança a nova janela. Ótimo para as necessidades do package.json do VS2015
TetraDev
13
Isso não funcionará se você tiver tarefas do observador, pois &&aguarda a conclusão do primeiro comando antes de iniciar o segundo comando e uma tarefa do observador nunca será concluída.
Benny Neugebauer
2
@BennyNeugebauer Os comandos são precedidos pelo comando "start", que abre uma nova linha de comando para cada um dos comandos. Também fiquei confuso no começo porque pensei que "o uso do operador && não funcionaria". Esta solução é muito simples e não requer pacotes adicionais / trabalho do desenvolvedor.
Addison
5
Isto está errado. O comando será executado sequencialmente. No Windows, você precisa usar um plug-in para executar comandos simultaneamente.
precisa saber é o seguinte
1
Isso não é específico do Windows?
binki
62

Você deve usar o npm-run-all (ou concurrently, parallelshell), porque ele tem mais controle sobre os comandos de iniciar e matar. Os operadores &, |são idéias ruins porque você vai precisar para pará-lo manualmente após todos os testes forem concluídos.

Este é um exemplo para teste de transferidor através do 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 = Execute comandos em paralelo.

-r = Mate todos os comandos quando um deles terminar com um código de saída zero.

A execução npm run testiniciará o driver Selenium, iniciará o servidor http (para servir os arquivos) e executará testes de transferidor. Após a conclusão de todos os testes, ele fechará o servidor http e o driver selênio.

nir
fonte
3
Eu me pergunto como isso funciona corretamente para a execução dos testes. Enquanto o webdriver-start e o http-server podem ser executados em paralelo, a tarefa do transferidor deve ser executada somente após os dois primeiros.
Asenovm
@asenovm para tarefas dependentes de pedidos, por que não usar gulpe gulp-sync?
R3wt
30

Você pode usar um &para script de execução paralela

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

Link de referência

Behnam Mohammadi
fonte
Isso também funcionará no Windows? Desculpe, sou muito novo no nó e não sei como verificar isso!
Benison Sam 14/01
@BenisonSam não, funciona no Mac
shanehoban 29/01
25

Uma solução melhor é usar &

"dev": "npm run start-watch & npm run wp-server"
Corey
fonte
54
Não, não é melhor porque não funciona em todas as plataformas.
Stijn de Witt
Eu não sabia disso. Em quais plataformas ele não funciona? @Corey - actualizar a sua resposta com o aviso na inter-op e eu vou upvote você
Ashley Coolman
8
&funciona no Windows, mas funciona de maneira diferente. No OSX, ele executará os dois comandos simultaneamente, mas no Windows, executará o primeiro comando e, depois que o primeiro comando existir, executará o segundo comando.
Trevor
3
Não, não é como desanexa o processo, você não poderá matá-lo de uma maneira simples.
Ncedman
2
@ngryman Isso é o que eu esperava também. No entanto, eu tentei isso e mata todos os três processos (dev, start-watch e wp-server) quando você pressiona Ctrl + C.
musicin3d
17

Eu verifiquei quase todas as soluções de cima e só com o npm-run-all consegui resolver todos os problemas. A principal vantagem sobre todas as outras soluções é a capacidade de executar scripts com 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",
}

Nota run-pé um atalho paranpm-run-all --parallel

Isso me permite executar o comando com argumentos como npm run test:watch -- Something.

EDITAR:

Há mais uma opção ú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.

Adicione -rao seu npm-run-allscript para eliminar todos os processos quando um terminar com o código 0. Isso é especialmente útil quando você executa um servidor HTTP e outro script que usa o servidor.

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

Eu tenho uma solução multiplataforma sem módulos adicionais . Eu estava procurando por algo como um bloco try catch que eu pudesse usar no cmd.exe e no bash.

A solução é command1 || command2que parece funcionar nos dois ambientes iguais. Portanto, a solução para o OP é:

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

Então simples npm start(e npm run dev) funcionará em todas as plataformas!

Entidade preta
fonte
11

Se você substituir o e comercial duplo por um único e comercial, os scripts serão executados simultaneamente.

Neil Girardi
fonte
Exatamente, é simples e elegante, sem necessidade de dependências ou outras magias.
precisa saber é o seguinte
1
@Ginzburg Porque não funciona da mesma forma para todas as plataformas, como você pode ver em outras respostas.
Jorge Fuentes González
6

Solução Rápida

Nesse caso, eu diria a melhor aposta Se esse script for para um módulo privado destinado a ser executado apenas em máquinas baseadas em * nix , você pode usar o operador de controle para processos de bifurcação, parecidos com o seguinte:&

Um exemplo de como fazer isso em um arquivo 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 &"
  }

Você os executaria em paralelo via npm run serve-bundle. Você pode aprimorar os scripts para gerar os pids do processo bifurcado em um arquivo como este:

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

Google algo como operador de controle bash para bifurcação para saber mais sobre como funciona. Também forneci algum contexto adicional sobre o aproveitamento das técnicas Unix nos projetos do Nó abaixo:

Contexto adicional RE: Ferramentas Unix e Node.js

Se você não estiver no Windows, as ferramentas / técnicas do Unix geralmente funcionam bem para obter algo com os scripts do Node porque:

  1. Grande parte do Node.js imita com amor os princípios do Unix
  2. Você está no * nix (incl. OS X) e o NPM está usando um shell de qualquer maneira

Módulos para tarefas do sistema em Nodeland também costumam ser abstrações ou aproximações de ferramentas Unix, de fsaté streams.

james_womack
fonte
1
Não, como o &operador não é suportado no Windows.
Stijn de Witt
3
@StijndeWitt minha postagem diz "Se você não está no Windows ...". 0% das pessoas com quem trabalho, em uma das maiores empresas de tecnologia do mundo, executa o Node no Windows. Claramente, meu post ainda é valioso para muitos desenvolvedores.
James_womack
2
É uma maneira circular de raciocinar, não é? Se você escrever seus scripts npm como este, não poderá usar o Windows porque não funcionará. Como ninguém usa o Windows, não importa se não funciona ... Você acaba com um software dependente da plataforma. Agora, se a coisa que precisa ser feita é muito difícil de executar em várias plataformas, isso pode ser uma boa escolha. Mas esse problema aqui é muito fácil de resolver com scripts npm padrão, como simultaneamente e paralelamente .
Stijn de Witt
2
@StijndeWitt Nenhum dos meus argumentos era circular. Fiz uma declaração de fato sem raciocínio. Estamos publicando técnicas comuns aos desenvolvedores do Node, muitos dos quais constroem e implantam em servidores Linux. Sim, ele deve funcionar no Windows se for um script da terra do usuário, mas a maioria dos scripts npm é para desenvolvimento e implantação - principalmente em máquinas * nix. Com relação aos módulos que você mencionou: a) é muito difícil chamar simultaneamente "standard" paralelamente e em paralelo (~ 1500 downloads por dia está longe do padrão no NPMland) eb) se você precisar de software adicional para um processo paralelo, poderá usar Gole.
james_womack
@StijndeWitt I apreciam estar cientes dessas módulos embora-obrigado
james_womack
6
npm-run-all --parallel task1 task2

editar:

Você precisa ter o npm-run-all instalado previamente. Verifique também esta página para outros cenários de uso.

noego
fonte
5

Que tal bifurcação

Outra opção para executar vários scripts de Nó é com um único script de Nó, que pode bifurcar muitos outros. A bifurcação é suportada nativamente no Node, portanto, não adiciona dependências e é multiplataforma.


Exemplo mínimo

Isso apenas executaria os scripts como estão e presume que eles estejam localizados no diretório do script pai.

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

Exemplo detalhado

Isso executaria os scripts com argumentos e configurados pelas várias opções disponíveis.

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

Comunicação com scripts bifurcados

A bifurcação também tem o benefício adicional de que o script pai possa receber eventos dos processos filho bifurcados, bem como enviar de volta. Um exemplo comum é o script pai matar seus filhos bifurcados.

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

Para mais eventos e métodos disponíveis, consulte a ChildProcessdocumentação

Boaz - Restabelecer Monica
fonte
3

Eu tive problemas com &e |, que saem de status e erros, respectivamente.

Outras soluções desejam executar qualquer tarefa com um nome específico, como npm-run-all, que não era meu caso de uso.

Então, eu criei o npm-run-parallel que executa scripts npm de forma assíncrona e relata quando eles terminam.

Portanto, para seus scripts, seria:

npm-run-parallel wp-server start-watch

ian
fonte
2

No meu caso, eu tenho dois projetos, um era UI e o outro era API , e ambos têm seu próprio script em seus respectivos package.jsonarquivos.

Então, aqui está o que eu fiz.

npm run --prefix react start&  npm run --prefix express start&
Vikash Mishra
fonte
Como sua solução. Também possui UI ( node app) e API (Angular em uma subpasta src , acho que é cd src/ng serve), apenas a primeira parte funciona. Por exemplo node app& cd src& ng serve.
Jeb50
1

Uso o npm-run-all há algum tempo, mas nunca me dei bem com ele, porque a saída do comando no modo de observação não funciona bem juntos. Por exemplo, se eu iniciar create-react-appe jestno modo de exibição, só poderei ver a saída do último comando executado. Então, na maioria das vezes, eu estava executando todos os meus comandos manualmente ...

É por isso que eu implemento minha própria lib, tela de execução . Ainda é um projeto muito jovem (de ontem: p), mas pode valer a pena examiná-lo, no seu caso seria:

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

Em seguida, pressione a tecla numérica 1para ver a saída de wp-servere pressione 0para ver a saída de start-watch.

Alexandre
fonte
1

Minha solução é semelhante à do Piittis, embora eu tenha tido alguns problemas ao usar o Windows. Então eu tive que validar para o 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");
})();

fonte
0

Script de nó simples para você continuar sem muita trabalheira. Usando o readline para combinar saídas para que as linhas não fiquem mutiladas.

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

esse trabalho no windows

SB3NDER
fonte