ChatGPT解决这个技术问题 Extra ChatGPT

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

在我的 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。此外,如果您的解决方案涉及构建工具,我宁愿使用 gulp 而不是 grunt,因为我已经在另一个项目中使用它。

&&按顺序 运行您的脚本,而 &并行 运行它们。
一种快速的方法是npm run start-watch & npm run wp-server。这会将第一个命令作为后台线程运行。当其中一个命令运行时间不长并且以后不需要手动退出时,这非常有效。 concurrently 之类的内容允许您使用 CTRL-C 同时终止所有线程。
@vsync 这适用于 Windows 吗?
@vsync 你确定吗?其他评论说这不是它的工作方式,它在实践中对我不起作用。
@Clonkex,是的 BUT 它不可靠,我使用 concurrently npm package 代替,效果很好,我只使用 Windows

S
Soviut

使用名为 concurrently 的包。

npm i concurrently --save-dev

然后像这样设置您的 npm run dev 任务:

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

node ./node_modules/concurrently/src/main.js 不是必需的。 concurrent 在脚本中可以正常工作,因为该模块将 bin 安装到 ./node_modules/.bin/concurrent
还有parallelshell。我实际上建议作为 concurrently 的一个使用与控制台输出混淆的多个流(颜色可能会变得奇怪,光标消失),而 parallelshell 没有 that issue
@StijndeWitt 同时提到的错误现已在 2.0.0 release 中修复。您可以使用 --raw 模式在输出中保留颜色。
@StijndeWitt parallelshell 已被弃用,取而代之的是 npm-run-all github.com/keithamus/…
我们必须有更好的方法来管理 Javascript 构建/运行脚本。这个平台的一切似乎都拼凑在一起。带有转义引号的引号和 npm 构建以调用其他“npm run”构建。这变得非常痛苦。
D
Diogo Cardoso

如果您使用的是类 UNIX 环境,只需使用 & 作为分隔符:

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

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

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

我这样做 - 有时当我“ctrl-c”npm 时,该命令一直在后台挂起......有什么想法吗?
a && ba 成功完成后启动 b,但 nodemon 永远不会在没有错误的情况下停止,因此无法工作。 a & b 启动 a,将其移至后台并立即启动 b。赢! a | ba 的标准输出通过管道传输到 b 的标准输入,这需要两者同时运行。尽管这似乎具有预期的效果,但您不应该在此处使用它。
@KamilTomšík & 是一个非常糟糕的主意,因为它分离了流程。这意味着 npm 将不再是父进程。你最终会得到一个不会被 ctrl-c 杀死的僵尸 npm run start-watch
只需添加 wait 即可缓解挂起进程的问题:"dev": "npm run start-watch & npm run wp-server & wait"
它不是僵尸。但是 unix 上的 & 会阻止命令响应 Cc/Cz,并且还会阻止其返回代码在发生故障时传播。
O
Oleg

在 windows cmd 中,您可以使用 start

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

以这种方式启动的每个命令都从它自己的窗口开始。


完美的解决方案!我喜欢它启动新窗口。非常适合 VS2015 package.json 需求
如果您有观察者任务,这将不起作用,因为 && 在开始第二个命令之前等待第一个命令完成,并且观察者任务永远不会完成。
@BennyNeugebauer 命令前面有“start”命令,它为每个命令打开一个新的命令行。一开始我也很困惑,因为我认为“使用 && 运算符不起作用”。这个解决方案非常简单,不需要开发人员额外的包/工作。
这是错误的。命令将按顺序运行。在 Windows 上,您必须使用插件才能同时运行命令。
它现在也意味着我必须在你的项目上使用 Windows 工作。
R
RyanZim

您应该使用 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 驱动程序。


不过,我想知道这如何正常运行测试。虽然 webdriver-start 和 http-server 可以并行运行,但量角器任务应该只在前两个之后运行。
@asenovm 用于订单相关任务,为什么不直接使用 gulpgulp-sync
J
Joe Chung

您可以将一个 & 用于并行运行脚本

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

Reference link


这也适用于 Windows 吗?对不起,我对节点很陌生,我不知道如何验证这一点!
@BenisonSam 我在我的 Windows 电脑上试过,即使使用单个“&”它也不会运行第二个命令
年前发布了相同的答案,并且得到的支持少于此。此外,已经充分讨论了为什么这种方法是一个坏主意。嗯,为什么这有这么多的赞成票,又一次?
@MartinBraun 快速简单
tnx - 这就是答案 - 所有其他提到的解决方案都是矫枉过正
C
Corey

更好的解决方案是使用 &

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

不,这不是更好,因为它不适用于所有平台。
我不知道。哪些平台不支持? @Corey - 用互操作警告更新你的答案,我会支持你
& 在 Windows 上工作,但工作方式不同。在 OSX 上,它将同时运行这两个命令,但在 Windows 上,它将运行第一个命令,并且在第一个命令存在后,它将运行第二个命令。
不,不是因为它分离了进程,你将无法以简单的方式杀死它。
@ngryman 这也是我的预期。但是,我尝试了这个,当您按下 Ctrl+C 时,它确实杀死了所有三个进程(dev、start-watch 和 wp-server)。
D
Darkowic

我检查了上面几乎所有的解决方案,只有使用 npm-run-all 我才能解决所有问题。与所有其他解决方案相比的主要优势是能够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",
}

注意 run-p 是 npm-run-all --parallel 的快捷方式

这允许我使用 npm run test:watch -- Something 之类的参数运行命令。

编辑:

npm-run-all 还有一个更有用的 option

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

另一个有用的选项是 -l 或 --print-labels - 它将任务名称打印为每行输出的前缀,以便您将它们区分开来。颜色也好看。
E
Entity Black

我有一个没有任何附加模块的跨平台解决方案。我一直在寻找可以在 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)将适用于所有平台!


|| 似乎在我的 Windows 10 PowerShell 上不起作用,但是,即使在 PowerShell 上,单个 | 似乎也能很好工作。我只用了两个命令进行了尝试,只能看到第二部分的输出,而不是第一部分的输出。
@HarshitGupta || 可能未在 Windows Ppowershell 中实现。显然它是在 PowerShell [Core] 7.0 中引入的,但可能不会被反向移植到 Windows Powershell 中。可悲的是,我的解决方案不是防弹的。
N
Neil Girardi

如果将双 & 号替换为单 & 号,脚本将同时运行。


确切地说,它简单而优雅,不需要依赖或其他魔法。
@Ginzburg 因为并非所有平台都适用,就像您在其他答案中看到的那样。
B
Boaz

怎么分叉

运行多个 Node 脚本的另一种选择是使用单个 Node 脚本,它可以fork许多其他脚本。 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 runningScripts= [];

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 documentation


e
eaorak
npm-run-all --parallel task1 task2

编辑:

您需要事先安装 npm-run-all。还要检查 this page 以了解其他使用场景。


j
james_womack

快速解决方案

在这种情况下,我会说最好的选择 如果这个脚本是一个私有模块,只在基于 *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",

谷歌一些类似 bash 控制操作符的 fork 来了解更多关于它是如何工作的。我还提供了一些关于在下面的 Node 项目中利用 Unix 技术的更多背景信息:

更多上下文 RE:Unix 工具和 Node.js

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

大部分 Node.js 都喜欢模仿 Unix 原则你在 *nix(包括 OS X)上,NPM 无论如何都在使用 shell

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


不,因为 Windows 不支持 & 运算符。
@StijndeWitt 我的帖子说“如果你不在 Windows 上……”。在世界上最大的科技公司之一,与我共事的人中有 0% 在 Windows 上运行 Node。很明显,我的帖子对许多开发人员来说仍然很有价值。
这是一种循环的推理方式,不是吗?如果您像这样编写您的 npm 脚本,您将无法能够使用 Windows,因为它无法工作。所以没有人使用Windows,所以它不起作用也没关系......你最终得到了平台相关的软件。现在,如果需要做的事情很难跨平台做,那么这可能是一个很好的权衡。但是这里的这个问题非常容易使用标准的 npm 脚本(例如 concurrentlyparallelshell)来解决。
@StijndeWitt 我的推理都不是循环的。我做了一个没有推理的事实陈述。我们发布了 Node 开发人员常用的技术,其中许多人在 Linux 服务器上构建和部署。是的,如果它是用户级脚本,它应该可以在 Windows 上运行,但是大多数 npm 脚本用于开发和部署——主要是在 *nix 机器上。关于您提到的模块a)同时调用parallelshell“标准”(每天约1500次下载远非NPMland的标准)和b)如果您需要额外的软件来进行并行处理,您不妨使用吞咽。
@StijndeWitt 我很高兴被告知这些模块 - 谢谢
A
Artem Belik
npm install npm-run-all --save-dev

包.json:

"scripts": {
  "start-watch": "...",
  "wp-server": "...",
  "dev": "npm-run-all --parallel start-watch wp-server"
}

更多信息:https://github.com/mysticatea/npm-run-all/blob/master/docs/npm-run-all.md


不,不是的。
g
gmspacex

只需将此 npm 脚本添加到根文件夹中的 package.json 文件即可。

{
  ...
  "scripts": {
    ...
    "start": "react-scripts start", // or whatever else depends on your project
    "dev": "(cd server && npm run start) & (cd ../client && npm run start)"
  }
}

M
Mosia Thabo

...但这将在运行 wp-server 之前等待 start-watch 完成。

为此,您必须在命令中使用 start。其他人已经说明了,但这就是它的工作方式,您的代码如下:

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

应该 :

"dev": " 启动 npm 运行 start-watch && 启动 npm 运行 wp-server"

这将做的是,它将为每个命令打开一个单独的实例并同时处理它们,就您的初始问题而言,这不应该是一个问题。我为什么这么说?这是因为这些实例都会在您只运行 1 条语句时自动打开,这是您的初始目标。


我得到了这个错误。从哪里开始命令。错误:在 processTicksAndRejections (internal/process/task_queues.js:82: 21)
M
Muhammad Muzamil

使用 npm 运行多个并行脚本的分步指南。全局安装 npm-run-all 包

npm i -g npm-run-all

现在在你的 package.json 存在的项目中安装并保存这个包

npm i npm-run-all --save-dev

现在以这种方式修改 package.json 文件中的脚本

"scripts": {
    "server": "live-server index.html",
    "watch": "node-sass scss/style.scss --watch",
    "all": "npm-run-all --parallel server watch"
},

现在运行这个命令

npm run all

在给定链接 npm-run-all 中有关此软件包的更多详细信息


i
ian

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

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

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

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

npm-run-parallel wp-server start-watch


佚名

我的解决方案类似于 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");
})();

S
SB3NDER

在父文件夹的 package.json 中:

"dev": "(cd api && start npm run start) & (cd ../client && start npm run start)"

这在 Windows 中工作


n
nick

这对我有用

{
"start-express": "tsc && nodemon dist/server/server.js",
"start-react": "react-scripts start",
"start-both": "npm -p -r run start-react && -p -r npm run start-express"
}

客户端和服务器都是用打字稿编写的。

React 应用程序是使用带有 typescript 模板的 create-react-app 创建的,并且位于默认的 src 目录中。

express在server目录下,入口文件是server.js

typescript 代码并转译成 js 放在 dist 目录下。

查看我的项目以获取更多信息:https://github.com/nickjohngray/staticbackeditor

更新:调用 npm run dev,开始工作

{"server": "tsc-watch --onSuccess \"node ./dist/server/index.js\"",
"start-server-dev": "npm run build-server-dev && node src/server/index.js",
"client": "webpack-dev-server --mode development --devtool inline-source-map --hot",
"dev": "concurrently \"npm run build-server-dev\"  \"npm run server\" \"npm run client\""}

我已经更新了我的脚本,我认为这是有效的,我在上面发布了我的更新
npm 上的 -p 和 -r 是什么?
C
Callat

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

所以,这就是我所做的。

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

喜欢你的解决方案。还有 UI (node app) 和 API(Angular 在子文件夹 src 中,猜测是 cd src/ng serve),只有第一部分有效.例如 node app& cd src& ng serve
P
Piittis

简单的节点脚本让您轻松进行。使用 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);
});

T
Thomas Ebert

我已经使用 npm-run-all 有一段时间了,但我一直不习惯它,因为在监视模式下命令的输出不能很好地协同工作。例如,如果我在监视模式下启动 create-react-appjest,我将只能看到我运行的最后一个命令的输出。所以大多数时候,我都是手动运行我所有的命令......

这就是我实现自己的库 run-screen 的原因。它仍然是一个非常年轻的项目(从昨天开始 :p )但它可能值得一看,在你的情况下它是:

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

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


我使用 npm-run-all 并在终端中获得两个进程的输出。
是的,我认为他们在处理输出的方式上做了一些更新,我最近使用 npm-run-all 并且到目前为止似乎工作得很好。
w
woutvdd

您还可以使用 prepost 作为特定脚本的前缀。

  "scripts": {
    "predev": "nodemon run-babel index.js &",
    "dev": "webpack-dev-server"
  }

然后运行:npm run dev


Y
Yilmaz

安装 npm install concurrently

"scripts": {
    "start:build": "tsc -w",
    "start:run": "nodemon build/index.js",
    "start": "concurrently  npm:start:*"
  },

A
AlexRMU

Windows CMD 的简单原生方式

"start /b npm run bg-task1 && start /b npm run bg-task2 && npm run main-task"

start /b 表示在后台启动)


这很好用,这也适用于 Windows。
J
Jamal

我认为最好的方法是使用 npm-run-all 如下:

1- npm install -g npm-run-all <--- 将全局安装
2- npm-run-all --parallel server client


E
Edward Brey

使用 concurrently 以与共享输出流并行运行命令。为了便于区分哪个输出来自哪个进程,请使用缩短的命令形式,例如 npm:wp-server。这会导致同时为每个输出行添加其命令名称的前缀。

package.json 中,您的脚本部分将如下所示:

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

P
Peter M. Elias

在 Linux 上仅使用 shell 脚本。

"scripts": {
  "cmd": "{ trap 'trap \" \" TERM; kill 0; wait' INT TERM; } && blocking1 & blocking2 & wait"
}

npm run cmd 然后 ^C 将杀死孩子并等待干净的退出。


Z
ZiiMakc

由于您可能需要向此脚本添加越来越多的内容,因此它将变得混乱且难以使用。如果您需要一些条件来检查,需要使用变量怎么办?所以我建议你看一下允许使用 js 创建脚本的google/zx

简单用法:

安装 zx: npm i -g zx add package.json 命令(可选,你可以将所有内容移动到脚本中):

  "scripts": {
    "dev": "zx ./scripts/dev.mjs", // run script
    "build:dev": "tsc -w", // compile in watch mode
    "build": "tsc", // compile
    "start": "node dist/index.js", // run
    "start:dev": "nodemon dist/index.js", // run in watch mode
  },

创建 dev.mjs 脚本文件:

#!/usr/bin/env zx

await $`yarn build`; // prebuild if dist is empty
await Promise.all([$`yarn start:dev`, $`yarn build:dev`]); // run in parallel

现在,每次您想启动开发服务器时,您只需运行 yarn devnpm run dev

它将首先编译 ts->js,然后在 watch 模式下并行运行 typescrpt 编译器和服务器。当您更改 ts 文件时->它将由 tsc 重新编译->nodemon 将重新启动服务器。

高级程序化使用

加载环境变量,在监视模式下编译 ts 并在更改时从 dist 重新运行服务器(dev.mjs):

#!/usr/bin/env zx
import nodemon from "nodemon";
import dotenv from "dotenv";
import path from "path";
import { fileURLToPath } from "url";

// load env variables
loadEnvVariables("../env/.env");

await Promise.all([
  // compile in watch mode (will recompile on changes in .ts files)
  $`tsc -w`,
  // wait for tsc to compile for first time and rerun server on any changes (tsc emited .js files)
  sleep(4000).then(() =>
    nodemon({
      script: "dist/index.js",
    })
  ),
]);

function sleep(ms) {
  return new Promise((resolve) => {
    setTimeout(resolve, ms);
  });
}

function getDirname() {
  return path.dirname(fileURLToPath(import.meta.url));
}

function loadEnvVariables(relativePath) {
  const { error, parsed } = dotenv.config({
    path: path.join(getDirname(), relativePath),
  });

  if (error) {
    throw error;
  }

  return parsed;
}