How can I run multiple npm scripts in parallel?

2019-01-01 07:55发布

问题:

In my package.json I have these two scripts:

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

I have to run these 2 scripts in parallel everytime I start developing in Node.js. The first thing I thought of was adding a third script like this:

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

... but that will wait for start-watch to finish before running wp-server.

How can I run these in parallel? Please keep in mind that I need to see the output of these commands. Also, if your solution involves a build tool, I\'d rather use gulp instead of grunt because I already use it in another project.

回答1:

Use a package called concurrently.

npm i concurrently --save-dev

Then setup your npm run dev task as so:

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


回答2:

Using the Concurrently package works, but you do not need it to accomplish this. You can just use a pipe on UNIX based machines run concurrent tasks. I would suggest this method over the other because it saves you from having to add an additional dependency.

\"dev\": \"npm run start-watch | npm run wp-server\"


回答3:

If you\'re using an UNIX-like environment, just use & as the separator:

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

Otherwise if you\'re interested on a cross-platform solution, you could use npm-run-all module:

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


回答4:

From windows cmd you can use start:

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

Every command launched this way starts in its own window.



回答5:

You should use npm-run-all (or concurrently, parallelshell), because it has more control over starting and killing commands. The operators &, | are bad ideas because you\'ll need to manually stop it after all tests are finished.

This is an example for protractor testing through 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 = Run commands in parallel.

-r = Kill all commands when one of them finishes with an exit code of zero.

Running npm run test will start Selenium driver, start http server (to serve you files) and run protractor tests. Once all tests are finished, it will close the http server and the selenium driver.



回答6:

A better solution is to use &

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


回答7:

If you replace the double ampersand with a single ampersand, the scripts will run concurrently.



回答8:

I\'ve checked almost all solutions from above and only with npm-run-all I was able to solve all problems. Main advantage over all other solution is an ability to run script with 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\",
}

Note run-p is shortcut for npm-run-all --paraller

This allows me to run command with arguments like npm run test:watch -- Something.

EDIT:

There is one more useful option for 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.

Add -r to your npm-run-all script to kill all processes when one finished with code 0. This is especially useful when you run a HTTP server and another script that use the server.

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


回答9:

I have a crossplatform solution without any additional modules. I was looking for something like a try catch block I could use both in the cmd.exe and in the bash.

The solution is command1 || command2 which seems to work in both enviroments same. So the solution for the OP is:

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

Then simple npm start (and npm run dev) will work on all platforms!



回答10:

Quick Solution

In this case, I\'d say the best bet If this script is for a private module intended to run only on *nix-based machines, you can use the control operator for forking processes, which looks like this: &

An example of doing this in a partial package.json file:

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

You\'d then execute them both in parallel via npm run serve-bundle. You can enhance the scripts to output the pids of the forked process to a file like so:

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

Google something like bash control operator for forking to learn more on how it works. I\'ve also provided some further context regarding leveraging Unix techniques in Node projects below:

Further Context RE: Unix Tools & Node.js

If you\'re not on Windows, Unix tools/techniques often work well to achieve something with Node scripts because:

  1. Much of Node.js lovingly imitates Unix principles
  2. You\'re on *nix (incl. OS X) and NPM is using a shell anyway

Modules for system tasks in Nodeland are also often abstractions or approximations of Unix tools, from fs to streams.



回答11:

npm-run-all --parallel task1 task2

edit:

You need to have npm-run-all installed beforehand. Also check this page for other usage scenarios.



回答12:

I ran into problems with & and |, which exit statuses and error throwing, respectively.

Other solutions want to run any task with a given name, like npm-run-all, which wasn\'t my use case.

So I created npm-run-parallel that runs npm scripts asynchronously and reports back when they\'re done.

So, for your scripts, it\'d be:

npm-run-parallel wp-server start-watch



回答13:

How about forking

Another option to run multiple Node scripts is with a single Node script, which can fork many others. Forking is supported natively in Node, so it adds no dependencies and is cross-platform.


Minimal example

This would just run the scripts as-is and assume they\'re located in the parent script\'s directory.

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

Verbose example

This would run the scripts with arguments and configured by the many available options.

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

Communicating with forked scripts

Forking also has the added benefit that the parent script can receive events from the forked child processes as well as send back. A common example is for the parent script to kill its forked children.

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

For more available events and methods see the ChildProcess documentation



回答14:

In my case I have two projects, one was UI and the other was API, and both have their own script in their respective package.json files.

So, here is what I did.

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