Как я могу запустить несколько сценариев npm параллельно?

543

По моему у package.jsonменя есть два скрипта:

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

Я должен запускать эти 2 сценария параллельно каждый раз, когда начинаю разработку в Node.js. Первое, о чем я подумал, это добавить третий скрипт:

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

... но это будет ждать start-watchдо конца, прежде чем бежать wp-server.

Как я могу запустить их параллельно? Пожалуйста, имейте в виду, что мне нужно увидеть outputэти команды. Кроме того, если ваше решение включает в себя инструмент для сборки, я бы предпочел использовать его, gulpа не gruntпотому, что я уже использую его в другом проекте.

Андре Пена
источник
23
&&будет запускать ваши скрипты последовательно, в то время как &будет запускать их параллельно .
Vsync
Быстрый способ сделать это npm run start-watch & npm run wp-server. Это запустит первую команду как фоновый поток. Это работает очень хорошо, когда одна из команд не выполняется долго и не требует ручного выхода позже. Нечто подобное concurrentlyпозволяет вам убить все потоки одновременно с помощью CTRL-C.
Джошуа Пинтер

Ответы:

617

Используйте пакет, который вызывается одновременно .

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
Рэйн
14
Существует также параллель . Я на самом деле рекомендую использовать тот, который concurrentlyиспользует несколько потоков, которые связываются с выводом на консоль (цвета могут выглядеть странно, курсор исчезает), тогда parallelshellкак такой проблемы нет .
Стейн де Витт
3
Ошибки, одновременно упоминаемые @StijndeWitt, теперь исправлены в версии 2.0.0 . Вы можете использовать --rawрежим, чтобы сохранить цвета на выходе.
Киммо
23
@StijndeWitt Parallelshell устарела в пользу npm-run-all github.com/keithamus/…
jtzero
12
У нас должен быть лучший способ управлять сценариями сборки / запуска Javascript. Все для этой платформы, похоже, скреплено. кавычки с экранированными кавычками и сборками npm для вызова других сборок 'npm run'. Это становится довольно болезненным.
Эндрю Т Финнелл
142

Если вы используете 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, команда продолжает зависать в фоновом режиме ... Есть идеи?
Камил Томшик
13
a && bзапускается bпосле aуспешного завершения, но nodemon никогда не останавливается без ошибок, так что это не может работать. a & bначинается a, перемещает его на задний план и начинает bсразу же. Выиграть! a | bТрубы, выход которых aк стандарту bтребует одновременной работы обоих. Хотя это может показаться желаемым эффектом, вы не должны использовать его здесь.
j2L4e
8
@ KamilTomšík &- действительно плохая идея, поскольку она отрывает процесс. Это означает, что npmбольше не будет родительским процессом. Вы получите зомби npm run start-watch, которого не убьют ctrl-c.
ngryman
6
Просто добавьте, waitчтобы смягчить проблему с зависающими процессами:"dev": "npm run start-watch & npm run wp-server & wait"
Руслан Прокопчук
2
Это не зомби. Но &в Unix предотвращает реакцию команды на Cc / Cz, а также предотвращает распространение ее кода возврата в случае сбоя.
Бинки
77

Из Windows CMD вы можете использовать start:

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

Каждая команда, запускаемая таким образом, запускается в собственном окне.

OV
источник
2
Идеальное решение! Мне нравится, что он запускает новое окно. Отлично подходит для VS2015 package.json
TetraDev
13
Это не работает, если у вас есть задачи наблюдателя, потому что &&ждет завершения первой команды перед началом второй команды, и задача наблюдателя никогда не завершится.
Бенни Нойгебауэр,
2
@BennyNeugebauer Командам предшествует команда «start», которая открывает новую командную строку для каждой из команд. Сначала я тоже растерялся, потому что думал, что «использование оператора && не сработает». Это решение очень простое и не требует никаких дополнительных пакетов / работы от разработчика.
Addison
5
Это не верно. Команда будет выполняться последовательно. В Windows вы должны использовать плагин для одновременного запуска команд.
Жекаус
1
Разве это не для Windows?
Бинки,
62

Вы должны использовать npm-run-all (или concurrently, parallelshell), потому что он имеет больший контроль над командами запуска и уничтожения. Операторы &, |плохие идеи , потому что вам нужно вручную остановить его после того, как все тесты закончены.

Это пример тестирования транспортира через 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-сервер и драйвер селена.

NIR
источник
3
Интересно, как это работает правильно для запуска тестов, хотя. Хотя webdriver-start и http-server могут работать параллельно, задача транспортира должна выполняться только после первых двух.
Асеновм
@asenovm для задач, зависящих от порядка, почему бы просто не использовать gulpи gulp-sync?
r3wt
30

Вы можете использовать один &для параллельного запуска скрипта

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

Ссылка ссылка

Бехнам Мохаммади
источник
Будет ли это работать в Windows? Извините, я новичок в узле и не знаю, как это проверить!
Бенисон Сэм
@BenisonSam нет, хотя работает на Mac
shanehoban
25

Лучшее решение - использовать &

"dev": "npm run start-watch & npm run wp-server"
Corey
источник
54
Нет, это не лучше, потому что это не работает на всех платформах.
Стейн де Витт
Я не знал этого. На каких платформах он не работает? @Corey - обнови свой ответ предупреждением об интероперации, и я тебя поддержу
Эшли Кулман,
8
&работает на Windows, но работает по-другому. В OSX он запускает обе команды одновременно, но в Windows он запускает первую команду, а после существования первой команды запускает вторую команду.
Тревор
3
Нет, это не так, как отсоединяет процесс, вы не сможете убить его простым способом.
ngryman
2
@ngryman Это то, что я ожидал тоже. Тем не менее, я попробовал это, и он убивает все три процесса (dev, start-watch и wp-server), когда вы нажимаете Ctrl + C.
musicin3d
17

Я проверил почти все решения сверху и только с помощью 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-pявляется ярлыком дляnpm-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 -- {*}\" --",
Darkowic
источник
15

У меня есть кроссплатформенное решение без каких-либо дополнительных модулей . Я искал что-то вроде блока try catch, который мог бы использовать как в cmd.exe, так и в bash.

Решение, command1 || command2которое, кажется, работает в обеих средах одинаково. Таким образом, решение для ОП:

"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 startnpm run dev) будут работать на всех платформах!

Entity Black
источник
11

Если вы замените двойной амперсанд одним амперсандом, сценарии будут выполняться одновременно.

Нил Джирарди
источник
Точно, это просто и элегантно, не нужно никаких зависимостей или другой магии.
magikMaker,
1
@Ginzburg Потому что не работает одинаково для всех платформ, как вы можете видеть в других ответах.
Хорхе Фуэнтес Гонсалес
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. Вы можете улучшить сценарии для вывода pids разветвленного процесса в файл следующим образом:

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

Google что-то вроде оператора управления bash за разветвление, чтобы узнать больше о том, как это работает. Я также предоставил некоторые дополнительные сведения об использовании технологий Unix в проектах Node ниже:

Дополнительный контекст RE: Unix Tools & Node.js

Если вы не используете Windows, инструменты / методики Unix часто хорошо работают для достижения чего-либо с помощью сценариев Node, потому что:

  1. Большая часть Node.js с любовью подражает принципам Unix
  2. Вы используете * nix (включая OS X), а NPM все равно использует оболочку

Модули для системных задач в Nodeland также часто являются абстракциями или приближениями инструментов Unix, от fsдо streams.

james_womack
источник
1
Нет, так как &оператор не поддерживается в Windows.
Стейн де Витт
3
@StijndeWitt мой пост говорит "Если вы не на Windows ...". 0% людей, с которыми я работаю, в одной из крупнейших технологических компаний мира, используют Node на Windows. Очевидно, что мой пост по-прежнему ценен для многих разработчиков.
james_womack
2
Это своего рода круговой способ рассуждения, не так ли? Если вы напишите свои сценарии npm, как это, вы не сможете использовать Windows, потому что она не будет работать. Таким образом, никто не использует Windows, так что не имеет значения, что она не работает ... Вы получаете программное обеспечение, зависящее от платформы. Теперь, если то, что нужно сделать, очень сложно сделать кроссплатформенным, то это может быть хорошим компромиссом. Но эту проблему очень легко решить с помощью стандартных сценариев npm, таких как одновременный и параллельный процессинг .
Стейн де Витт
2
@StijndeWitt Ни одно из моих рассуждений не было круглым. Я сделал констатацию факта без рассуждений. Мы публикуем методы, общие для разработчиков Node, многие из которых создают и разворачивают на серверах Linux. Да, он должен работать в Windows, если это пользовательский сценарий, но большинство сценариев npm предназначены для разработки и развертывания - в основном на компьютерах * nix. Что касается модулей, о которых вы упомянули, то а) это огромная протяженность одновременного вызова и параллельной оболочки "стандарт" (~ 1500 загрузок в день далеко не стандартно в NPMland) и б) если вам нужно дополнительное программное обеспечение для параллельного процесса, вы также можете использовать Глоток.
james_womack
@StijndeWitt Я ценю, что мне сообщили об этих модулях - спасибо
james_womack
6
npm-run-all --parallel task1 task2

редактировать:

Вам необходимо предварительно установить npm-run-all . Также проверьте эту страницу для других сценариев использования.

noego
источник
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
});

Общение с помощью разветвленных скриптов

Forking также имеет дополнительное преимущество, заключающееся в том, что родительский скрипт может получать события от разветвленных дочерних процессов, а также отправлять их обратно. Типичным примером является родительский скрипт для уничтожения его разветвленных потомков.

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

Для более доступных событий и методов см. ChildProcessДокументацию

Вооз - восстановить Монику
источник
3

Я столкнулся с проблемами с &и |, которые выходят из состояний и выдачи ошибок, соответственно.

Другие решения хотят запускать любую задачу с заданным именем, например, npm-run-all, что не было моим вариантом использования.

Поэтому я создал npm-run -rallel, который запускает сценарии npm асинхронно и сообщает о завершении работы.

Итак, для ваших сценариев это будет:

npm-run-parallel wp-server start-watch

Иан
источник
2

В моем случае у меня есть два проекта, один из которых был 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.
Jeb50
1

Я использовал npm-run-all в течение некоторого времени, но я никогда не ладил с ним, потому что вывод команды в режиме просмотра не очень хорошо работает вместе. Например, если я начну create-react-appи jestв режиме просмотра, я смогу увидеть вывод только из последней выполненной команды. Поэтому большую часть времени я выполнял все свои команды вручную ...

Вот почему я реализую свой собственный lib, run-screen . Это еще очень молодой проект (со вчерашнего дня: 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);
});
Piittis
источник
0
"dev": "(cd api && start npm run start) & (cd ../client && start npm run start)"

эта работа в windows

SB3NDER
источник