Jak mogę uruchomić wiele skryptów npm równolegle?

542

W moim package.jsonmam te dwa skrypty:

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

Te 2 skrypty muszę uruchamiać równolegle za każdym razem, gdy zaczynam programować w Node.js. Pierwszą rzeczą, o której myślałem, było dodanie trzeciego skryptu takiego:

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

... ale będzie czekać na start-watchzakończenie przed uruchomieniem wp-server.

Jak mogę je uruchomić równolegle? Pamiętaj, że muszę zobaczyć outputte polecenia. Ponadto, jeśli twoje rozwiązanie wymaga narzędzia do kompilacji, wolę użyć gulpzamiast tego, gruntponieważ używam go już w innym projekcie.

André Pena
źródło
23
&&uruchomi twoje skrypty sekwencyjnie, jednocześnie &uruchomi je równolegle .
vsync
Szybki sposób to zrobić npm run start-watch & npm run wp-server. Spowoduje to uruchomienie pierwszego polecenia jako wątku w tle. Działa to naprawdę dobrze, gdy jedno z poleceń nie jest długo uruchamiane i nie trzeba później ręcznie go opuszczać. Coś takiego concurrentlypozwala zabić wszystkie wątki jednocześnie za pomocą CTRL-C.
Joshua Pinter

Odpowiedzi:

616

Użyj pakietu o nazwie współbieżnej .

npm i concurrently --save-dev

Następnie skonfiguruj swoje npm run devzadanie w następujący sposób:

"dev": "concurrently --kill-others \"npm run start-watch\" \"npm run wp-server\""
Neil Kistner
źródło
11
node ./node_modules/concurrently/src/main.jsnie jest potrzebne. concurrentbędzie działał dobrze w skryptach, ponieważ moduł instaluje bin do./node_modules/.bin/concurrent
raine
14
Istnieje również równoległa powłoka . Naprawdę polecam, ponieważ jeden concurrentlyużywa wielu strumieni, które psują się z wyjściem konsoli (kolory mogą się dziwnie zabraknąć, kursor zniknie), parallelshellale nie ma tego problemu .
Stijn de Witt
3
Błędy wspomniane jednocześnie przez @StijndeWitt zostały teraz naprawione w wersji 2.0.0 . Możesz użyć --rawtrybu, aby zachować kolory na wydruku.
Kimmo,
23
@StijndeWitt Parallelshell został wycofany na korzyść npm-run-all github.com/keithamus/…
jtzero
12
Musi istnieć lepszy sposób zarządzania skryptami kompilacji / uruchamiania JavaScript. Wszystko na tej platformie wydaje się powiązane. cytaty ze znakami ucieczki i kompilacjami npm, aby wywołać inne kompilacje „uruchom npm”. Jest to dość bolesne.
Andrew T Finnell
141

Jeśli używasz środowiska podobnego do UNIX, po prostu użyj &jako separatora:

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

W przeciwnym razie, jeśli jesteś zainteresowany rozwiązaniem wieloplatformowym, możesz użyć npm-run-all module:

"dev": "npm-run-all --parallel start-watch wp-server"
Diogo Cardoso
źródło
14
Robię to - od czasu do czasu, gdy „ctrl-c” npm, polecenie wciąż się trzyma w tle ... Jakieś pomysły?
Kamil Tomšík
13
a && buruchamia się bpo apomyślnym zakończeniu, ale nodemon nigdy nie zatrzymuje się bez błędów, więc to nie działa. a & buruchamia się a, przenosi go w tło i zaczyna bod razu. Zdobyć! a | bpotoki, których stdout ajest do stdin, bktórych oba wymagają jednoczesnego działania. Chociaż może to wydawać się pożądanym efektem, nie powinieneś go tutaj używać.
j2L4e,
8
@ KamilTomšík &to naprawdę zły pomysł, ponieważ oddziela ten proces. Oznacza to, że npmnie będzie to już proces nadrzędny. Skończysz z zombie, npm run start-watchktórego nie zabijesz ctrl-c.
ngryman
6
Wystarczy dodać, waitaby złagodzić problem z procesami zawieszania:"dev": "npm run start-watch & npm run wp-server & wait"
Ruslan Prokopchuk
2
To nie jest zombie. Ale &w systemie uniksowym komenda nie reaguje na polecenie Cc / Cz, a także zapobiega propagacji kodu powrotu w przypadku awarii.
binki
77

Z Windows cmd możesz użyć start:

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

Każde polecenie uruchomione w ten sposób rozpoczyna się we własnym oknie.

ov
źródło
2
Idealne rozwiązanie! Uwielbiam to, że uruchamia nowe okno. Idealne dla potrzeb pakietu.j.js.Jet
TetraDev
13
Nie działa to, jeśli masz zadania obserwatora, ponieważ &&czeka ono na zakończenie pierwszego polecenia przed uruchomieniem drugiego polecenia, a zadanie obserwatora nigdy się nie zakończy.
Benny Neugebauer
2
@BennyNeugebauer Polecenia są poprzedzone poleceniem „start”, które otwiera nowy wiersz poleceń dla każdego z poleceń. Początkowo byłem również zdezorientowany, ponieważ myślałem, że „użycie operatora && nie zadziała”. To rozwiązanie jest bardzo proste i nie wymaga żadnych dodatkowych pakietów / prac od dewelopera.
Addison,
5
To jest źle. Polecenie będzie uruchamiane sekwencyjnie. W systemie Windows musisz użyć wtyczki, aby uruchamiać polecenia jednocześnie.
zhekaus
1
Czy to nie jest specyficzne dla systemu Windows?
binki
62

Powinieneś użyć npm-run-all (lub concurrently, parallelshell), ponieważ ma on większą kontrolę nad poleceniami uruchamiania i zabijania. Operatorzy &,| są złe pomysły, ponieważ trzeba ręcznie zatrzymać go po zakończeniu wszystkich testów.

To jest przykład testowania kątomierza za pomocą 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 = Uruchom polecenia równolegle.

-r = Zabij wszystkie polecenia, gdy jedno z nich zakończy się kodem wyjścia równym zero.

Uruchomienie npm run testuruchomi sterownik Selenium, serwer HTTP (do obsługi plików) i uruchomi testy kątomierza. Po zakończeniu wszystkich testów zamknie serwer http i sterownik selenu.

nir
źródło
3
Zastanawiam się jednak, jak to działa poprawnie do uruchamiania testów. Chociaż webdriver-start i serwer HTTP mogą działać równolegle, zadanie kątomierza powinno zostać uruchomione dopiero po pierwszych dwóch.
asenovm
@asenovm dla zadań zależnych od zamówienia, dlaczego nie użyć gulpi gulp-sync?
r3wt
30

Możesz użyć jednego &do skryptu równoległego

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

Link referencyjny

Behnam Mohammadi
źródło
Czy to zadziała również w systemie Windows? Przepraszam, jestem całkiem nowy w węźle i nie wiem, jak to sprawdzić!
Benison Sam
@BenisonSam nie, działa jednak na komputerach Mac
shanehoban
25

Lepszym rozwiązaniem jest użycie &

"dev": "npm run start-watch & npm run wp-server"
Corey
źródło
54
Nie, nie jest lepiej, ponieważ nie działa na wszystkich platformach.
Stijn de Witt
Nie wiedziałem tego. Na jakich platformach nie działa? @Corey - zaktualizuj swoją odpowiedź z ostrzeżeniem o inter-op, a ja cię poprę
Ashley Coolman
8
&działa w systemie Windows, ale działa inaczej. W OSX uruchomi oba polecenia jednocześnie, ale w systemie Windows uruchomi pierwsze polecenie, a po istnieniu pierwszego polecenia uruchomi drugie polecenie.
Trevor
3
Nie, nie jest tak, ponieważ oddziela proces, nie będziesz w stanie go zabić w prosty sposób.
ngryman
2
@ngryman Tego też się spodziewałem. Jednak próbowałem tego i zabija wszystkie trzy procesy (dev, start-watch i wp-server) po naciśnięciu Ctrl + C.
musicin3d
17

Sprawdziłem prawie wszystkie rozwiązania z góry i tylko z npm-run-all udało mi się rozwiązać wszystkie problemy. Główną przewagą nad wszystkimi innymi rozwiązaniami jest możliwość uruchamiania skryptu z argumentami .

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

Uwaga run-pjest skrótem donpm-run-all --parallel

To pozwala mi uruchamiać polecenie z argumentami takimi jak npm run test:watch -- Something.

EDYTOWAĆ:

Jest jeszcze jedna przydatna opcja dla 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.

Dodaj -rdo npm-run-allskryptu, aby zabić wszystkie procesy, gdy skończysz z kodem 0. Jest to szczególnie przydatne, gdy uruchamiasz serwer HTTP i inny skrypt korzystający z serwera.

  "test": "run-p -r test:static-server \"test:jest -- {*}\" --",
Darkowic
źródło
15

Mam rozwiązanie typu crosssplatform bez żadnych dodatkowych modułów . Szukałem czegoś w rodzaju bloku catch catch, którego mógłbym użyć zarówno w cmd.exe, jak i w bashu.

command1 || command2Wydaje się, że rozwiązanie działa tak samo w obu środowiskach. Tak więc rozwiązaniem dla PO jest:

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

Wtedy prosty npm start(i npm run dev) będzie działał na wszystkich platformach!

Czarny podmiot
źródło
11

Jeśli zastąpisz podwójny znak ampersand pojedynczym znakiem ampersand, skrypty będą działać jednocześnie.

Neil Girardi
źródło
Dokładnie, jest to proste i eleganckie, nie wymaga zależności ani innej magii.
magikMaker
1
@Ginzburg Ponieważ nie działa tak samo na wszystkich platformach, jak widać w innych odpowiedziach.
Jorge Fuentes González,
6

Szybkie rozwiązanie

W takim przypadku powiedziałbym, że najlepszy wybór Jeśli ten skrypt jest przeznaczony dla modułu prywatnego przeznaczonego do uruchamiania tylko na komputerach z * nix , możesz użyć operatora sterującego do rozwidlania procesów, który wygląda następująco:&

Przykład wykonania tego w częściowym pliku package.json:

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

Następnie wykonałeś je oba równolegle przez npm run serve-bundle. Możesz ulepszyć skrypty, aby wypisywać pid z rozwidlonego procesu do pliku takiego jak:

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

Google coś jak operator kontroli bash za rozwidlanie aby dowiedzieć się więcej o tym, jak to działa. Przedstawiłem również trochę dalszego kontekstu dotyczącego wykorzystania technik uniksowych w projektach Node:

Dalszy kontekst RE: Unix Tools & Node.js

Jeśli nie korzystasz z systemu Windows, narzędzia / techniki uniksowe często działają dobrze, aby osiągnąć coś za pomocą skryptów Node, ponieważ:

  1. Znaczna część Node.js naśladuje zasady uniksowe
  2. Jesteś na * nix (włącznie z OS X), a NPM i tak używa powłoki

Moduły do ​​zadań systemowych w Nodeland są często abstrakcjami lub aproksymacjami narzędzi uniksowych od fsdo streams.

james_womack
źródło
1
Nie, ponieważ &operator nie jest obsługiwany w systemie Windows.
Stijn de Witt
3
@StijndeWitt mój post mówi „Jeśli nie masz systemu Windows ...”. 0% osób, z którymi współpracuję, w jednej z największych firm technologicznych na świecie, korzysta z Node w systemie Windows. Więc oczywiście mój post jest nadal cenny dla wielu programistów.
james_womack
2
To rodzaj okrągłego sposobu rozumowania, prawda? Jeśli napiszesz takie skrypty npm, nie będziesz mógł korzystać z systemu Windows, ponieważ nie będzie działać. Tak więc nikt nie używa systemu Windows, więc nie ma znaczenia, że ​​nie działa ... Skończysz z oprogramowaniem zależnym od platformy. Teraz, jeśli rzecz, którą należy zrobić, jest bardzo trudna do zrobienia na różnych platformach, może to być dobry kompromis. Ale ten problem tutaj jest bardzo łatwy do zrobienia ze standardowymi skryptami npm, takimi jak współbieżnie i równolegle .
Stijn de Witt
2
@StijndeWitt Żadne z moich rozumowań nie było okrągłe. Złożyłem oświadczenie bez uzasadnienia. Publikujemy techniki wspólne dla programistów Node, z których wielu tworzy i wdraża na serwerach Linux. Tak, powinien działać w systemie Windows, jeśli jest to skrypt użytkownika, ale większość skryptów npm jest przeznaczona do programowania i wdrażania - głównie na komputerach * nix. Jeśli chodzi o moduły, o których wspomniałeś: a) jest to ogromne rozciąganie się na jednoczesne wywoływanie i równoległe „standardowe” (~ 1500 pobrań dziennie jest dalekie od standardowego w NPMland) ib) jeśli potrzebujesz dodatkowego oprogramowania do równoległego procesu, równie dobrze możesz użyć Łyk.
james_womack
@StijndeWitt Doceniam fakt, że dowiedziałem się o tych modułach - dziękuję
james_womack
6
npm-run-all --parallel task1 task2

edytować:

Musisz wcześniej zainstalować npm-run-all . Sprawdź również tę stronę pod kątem innych scenariuszy użytkowania.

noego
źródło
5

Co powiesz na rozwidlenie

Inną opcją uruchamiania wielu skryptów Node jest jeden skrypt Node, który może rozwidlać wiele innych. Rozwidlenie jest obsługiwane natywnie w Węźle, więc nie dodaje żadnych zależności i jest wieloplatformowe.


Minimalny przykład

Spowodowałoby to po prostu uruchomienie skryptów bez zmian i przyjęcie, że znajdują się one w katalogu skryptu nadrzędnego.

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

Pełen przykład

Spowodowałoby to uruchomienie skryptów z argumentami i skonfigurowanych według wielu dostępnych opcji.

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

Komunikacja za pomocą rozwidlonych skryptów

Forking ma również tę dodatkową zaletę, że skrypt nadrzędny może odbierać zdarzenia z rozwidlonych procesów potomnych, a także odsyłać. Typowym przykładem jest skrypt nadrzędny, który zabija rozwidlone dzieci.

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

Więcej dostępnych zdarzeń i metod znajduje się w ChildProcessdokumentacji

Boaz - Przywróć Monikę
źródło
3

Wystąpiły problemy z &i |, które odpowiednio wychodzą ze stanu i zgłaszają błędy.

Inne rozwiązania chcą uruchomić dowolne zadanie o podanej nazwie, np. Npm-run-all, co nie było moim przypadkiem użycia.

Więc stworzyłem npm-run-parallel, który uruchamia skrypty npm asynchronicznie i raportuje, kiedy są gotowe.

W przypadku twoich skryptów byłoby to:

npm-run-parallel wp-server start-watch

Ian
źródło
2

W moim przypadku mam dwa projekty, jeden to interfejs użytkownika, a drugi to interfejs API , a oba mają własny skrypt w odpowiednimpackage.json plikach.

Oto co zrobiłem.

npm run --prefix react start&  npm run --prefix express start&
Vikash Mishra
źródło
Jak twoje rozwiązanie. Posiadają również UI ( node app) i API (Angular w podfolderze src , zgadnij, że cd src/ng serve), działa tylko pierwsza część. Na przykład node app& cd src& ng serve.
Jeb50
1

Używam npm-run-all od jakiegoś czasu, ale nigdy się z tym nie zgadzałem, ponieważ dane wyjściowe polecenia w trybie oglądania nie działają dobrze razem. Na przykład, jeśli zacznę create-react-appijest w trybie zegarka, będę mógł zobaczyć dane wyjściowe z ostatniego uruchomionego polecenia. Przez większość czasu wszystkie moje polecenia były uruchamiane ręcznie ...

Dlatego implementuję własną bibliotekę lib, run-screen . To wciąż bardzo młody projekt (od wczoraj: p), ale warto na niego spojrzeć, w twoim przypadku byłoby to:

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

Następnie naciskasz klawisz numeryczny, 1aby zobaczyć dane wyjściowe, wp-serveri naciśnij, 0aby zobaczyć dane wyjściowe start-watch.

Alexandre
źródło
1

Moje rozwiązanie jest podobne do rozwiązania Piittis, chociaż miałem problemy z używaniem systemu Windows. Musiałem więc zweryfikować pod kątem 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");
})();

źródło
0

Prosty skrypt węzła, abyś mógł zacząć bez większych problemów. Używanie readline do łączenia wyników, aby linie nie były zniekształcone.

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
źródło
0
"dev": "(cd api && start npm run start) & (cd ../client && start npm run start)"

ta praca w systemie Windows

SB3NDER
źródło