如何并行运行多个npm脚本?


542

在我的package.json我有这两个脚本:

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

每当我开始在Node.js中开发时,我必须并行运行这两个脚本。我想到的第一件事是添加第三个脚本,如下所示:

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

...但这将等待start-watch完成再运行wp-server

如何并行运行它们?请记住,我需要查看output以下命令。另外,如果您的解决方案涉及构建工具,则我宁愿使用gulpgrunt因为我已经在另一个项目中使用了它。


23
&&将按顺序运行脚本,而&并行运行它们。
vsync

一种快速的方法是npm run start-watch & npm run wp-server。这将第一个命令作为后台线程运行。当其中一个命令没有长时间运行并且以后不需要手动退出时,此方法非常有效。类似的东西concurrently允许您使用CTRL-C同时杀死所有线程。
约书亚·品特

Answers:


616

使用一个并发的包。

npm i concurrently --save-dev

然后按以下步骤设置您的npm run dev任务:

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

11
node ./node_modules/concurrently/src/main.js不需要。 concurrent将在脚本中正常工作,因为该模块安装了一个垃圾箱以./node_modules/.bin/concurrent
Raine 2015年

14
还有平行壳。实际上,我建议concurrently使用as作为多个流,这些流会干扰控制台的输出(颜色可能会变得很奇怪,光标消失了),而parallelshell没有这个问题
Stijn de Witt

3
@StijndeWitt同时提到的错误现已在2.0.0版本中修复。您可以使用--raw模式在输出中保留颜色。
Kimmo

23
@StijndeWitt parallelshell已被弃用,而推荐使用npm-run-allgithub.com/keithamus/…–
jtzero

11
我们必须有更好的方法来管理Javascript构建/运行脚本。该平台的所有内容似乎都固定在一起。带转义引号和npm构建的引号来调用其他“ npm run”构建。这变得非常痛苦。
Andrew T Finnell,

141

如果您使用的是类似UNIX的环境,则只需将其&用作分隔符即可:

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

否则,如果您对跨平台解决方案感兴趣,则可以使用npm-run-all 模块:

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

14
我这样做-不时“ ctrl -c” npm,命令一直挂在后台...有什么想法吗?
卡米尔·汤姆斯克(KamilTomšík),2016年

13
a && bba成功完成之后启动,但nodemon绝不会无误停止,因此无法正常工作。 a & b开始a,将其移至背景并立即开始b。赢得!a | b将stdout的标准输出a传递到标准输出,b同时要求两者同时运行。尽管这似乎可以达到预期的效果,但是您不应该在这里使用它。
j2L4e

8
@KamilTomšík &是一个非常糟糕的主意,因为它使过程分离。这意味着npm它将不再是父进程。您最终将遇到一个npm run start-watch不会被杀死的僵尸ctrl-c
ngryman '16

6
只是增加wait以减轻悬挂过程的问题:"dev": "npm run start-watch & npm run wp-server & wait"
Ruslan Prokopchuk

2
这不是僵尸。但是&在unix上,不仅可以防止命令对Cc / Cz作出响应,还可以防止失败时传播其返回代码。
宾基

77

在Windows cmd中,您可以使用start

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

以这种方式启动的每个命令都在其自己的窗口中启动。


2
完美的解决方案!我喜欢它会启动新窗口。非常适合VS2015 package.json需求
2016年

13
如果您有观察者任务,则此方法不起作用,因为&&在启动第二个命令之前,先等待第一个命令完成,并且观察者任务将永远不会完成。
Benny Neugebauer

2
@BennyNeugebauer命令之前带有“开始”命令,该命令为每个命令打开新的命令行。一开始我也很困惑,因为我认为“使用&&运算符将不起作用”。该解决方案非常简单,不需要开发人员提供额外的软件包/工作。
艾迪生

5
错了 命令将按顺序运行。在Windows上,您必须使用插件才能同时运行命令。
zhekaus

1
这不是特定于Windows的吗?
宾基

62

您应该使用npm-run-all(或concurrentlyparallelshell),因为它可以更好地控制启动和终止命令。运营商&|是坏的想法,因为你需要手动停止所有的测试都完成之后。

这是通过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 =并行运行命令。

-r =当其中一个命令的退出代码为零时,终止所有命令。

运行npm run test将启动Selenium驱动程序,启动http服务器(为您提供文件)并运行量角器测试。一旦完成所有测试,它将关闭http服务器和selenium驱动程序。


3
我不知道这如何正常运行测试。虽然webdriver-start和http-server可以并行运行,但量角器任务应仅在前两个任务之后运行。
asenovm '16

@asenovm用于与订单有关的任务,为什么不只使用gulpand gulp-sync
r3wt


25

更好的解决方案是使用 &

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

54
不,这不是更好,因为它不能在所有平台上都起作用。
Stijn de Witt

我不知道。它在什么平台上不起作用?@Corey-使用互操作上的警告更新您的答案,我将投票给您
Ashley Coolman

8
&在Windows上工作,但工作方式不同。在OSX上,它将同时运行两个命令,但是在Windows上,它将运行第一个命令,并且在第一个命令存在后,它将运行第二个命令。
Trevor

3
不,这不是因为它使流程分离,您将无法以简单的方式将其杀死。
ngryman '16

2
@ngryman这也是我所期望的。但是,我尝试了此操作,当您按Ctrl + C时,它确实杀死了所有三个进程(开发,启动监视和wp服务器)。
musicin3d'4

16

我已经从上面检查了几乎所有解决方案,仅使用npm-run-all我就能解决所有问题。与所有其他解决方案相比,主要优点是可以运行带有参数的脚本

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

注意run-pnpm-run-all --parallel

这使我可以使用类似的参数运行命令npm run test:watch -- Something

编辑:

还有一个有用的选项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.

添加-r到您的npm-run-all脚本中以在代码完成后杀死所有进程0。当您运行HTTP服务器和其他使用该服务器的脚本时,此功能特别有用。

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

15

我有一个没有任何其他模块跨平台解决方案。我一直在寻找可以在cmd.exe和bash中同时使用的try catch块之类的东西。

解决方案command1 || command2似乎在两个环境中都相同。因此,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"
}

然后简单 npm start(和npm run dev)就可以在所有平台上运行!



6

快速解决方案

在这种情况下,最好的选择是: 如果此脚本用于仅在基于* nix的计算机上运行的私有模块,则可以使用控制运算符进行分叉,如下所示:&

在部分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 &"
  }

然后,您可以通过并行执行它们npm run serve-bundle。您可以增强脚本,以将分叉进程的pid输出到文件,如下所示:

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

Google之类的bash控制运算符以了解其工作原理的更多信息。我还在下面的Node项目中提供了有关利用Unix技术的更多信息:

进一步的内容RE:Unix工具和Node.js

如果您不在Windows上,则Unix工具/技术通常可以很好地通过Node脚本实现某些目的,因为:

  1. 许多Node.js都在模仿Unix原则
  2. 您正在使用* nix(包括OS X),并且NPM仍在使用Shell

Nodeland中用于系统任务的模块通常也是Unix工具的抽象或近似,从fsstreams


1
&否,因为Windows不支持该运算符。
Stijn de Witt

3
@StijndeWitt我的帖子说“如果您不在Windows上...”。在全球最大的科技公司之一中,与我合作的人中有0%在Windows上运行Node。显然,我的帖子对许多开发人员仍然有价值。
james_womack '16

2
这是一种循环推理,不是吗?如果您编写的脚本故宫这样你就不会能够使用Windows,因为它不会工作。因此,没有人使用Windows,因此它不起作用并不重要...您最终获得了依赖平台的软件。现在,如果需要做的事情很难跨平台完成,那么这将是一个很好的权衡。但是,使用标准npm脚本(例如并发parallelshell非常容易解决此问题。
Stijn de Witt

2
@StijndeWitt我的推理都不是循环的。我没有事实就声明了理由。我们将发布Node开发人员常用的技术,其中许多开发人员都在Linux服务器上构建和部署。是的,如果它是一个userland脚本,它应该可以在Windows上运行,但是大多数npm脚本是用于开发和部署的-大多是在* nix机器上。关于您提到的模块a)同时调用并行外壳和“标准”(在NPMland中,每天约1500次下载远非标准)和b)如果需要并行过程的其他软件,则需要花费大量时间喝了
james_womack's

@StijndeWitt我欣赏能够意识到这些模块的,虽然,谢谢
james_womack


5

怎么样

运行多个Node脚本的另一种选择是使用单个Node脚本,它可以派生许多其他脚本。Node本身支持分叉,因此它不添加任何依赖关系并且是跨平台的。


最小的例子

这只会按原样运行脚本,并假定它们位于父脚本的目录中。

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

详细的例子

这将运行带有参数的脚本,并由许多可用选项进行配置。

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

与分叉脚本进行通信

分叉还具有额外的好处,即父脚本可以接收来自分叉的子进程的事件并发回。一个常见的示例是父脚本杀死其分叉的子代。

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

有关更多可用事件和方法,请参阅ChildProcess文档


3

我遇到了&和的问题|,分别退出状态和错误抛出。

其他解决方案希望以给定名称运行任何任务,例如npm-run-all,这不是我的用例。

因此,我创建了npm-run-parallel,它异步运行npm脚本并在完成后报告。

因此,对于您的脚本,将是:

npm-run-parallel wp-server start-watch


2

就我而言,我有两个项目,一个是UI,另一个是API,两个项目在各自的package.json文件中都有自己的脚本。

所以,这就是我所做的。

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

喜欢你的解决方案。也具有UInode app)和API(子文件夹src中的 Angular ,猜测是cd src/ng serve),只有第一部分有效。例如node app& cd src& ng serve
Jeb50 '19

1

我一直在使用npm-run-all一段时间,但是我从未与之相处,因为在监视模式下命令的输出不能很好地协同工作。例如,如果我启动create-react-appjest处于监视模式,则只能看到我运行的最后一条命令的输出。所以大多数时候,我都是手动运行所有命令...

这就是为什么我实现自己的lib 运行屏幕。它仍然是一个非常年轻的项目(从昨天开始:p),但是值得一看,在您的情况下,它将是:

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

然后按数字键1查看的输出,wp-server然后按0查看的输出start-watch


1

我的解决方案类似于Piittis,尽管使用Windows时遇到了一些问题。所以我必须验证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

简单的节点脚本,无需太多麻烦即可开始工作。使用readline合并输出,这样行就不会混乱。

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

Windows中的这项工作

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.