NodeJS: как правильно запустить spawn параллельно? - PullRequest
0 голосов
/ 24 октября 2018

Я запускаю цикл for, в котором выполняется следующее:

let perform_vrp = function() {
    //..
    perform_tsp();
}

let perform_tsp = function() {
  //..

  const pyProg = spawn('python3', [process.env.PWD + '/server/vrp_solver/tsp_solver.py', '/../../route_data/' + depot.city + '/' + moment(route.date_driven).format('Y-MM-DD'), 'morning',route.name]);

  winston.info('Solving the TSP for %s...', route.name);

  pyProg.stdout.on('data', function (data) {

    let result_string = data.toString();
    winston.info('Route result for %s is: %s', route.name, result_string);

    let result_array = eval(result_string); 
    //...
  });
}

В основном он вызывает скрипт python для каждого элемента цикла for.

Однако, когда один сценарий завершен, он также завершает все остальные и продолжает стандартный вывод с теми же «данными» для всех элементов цикла for.

Как я могу предотвратить это и позволить stdout дождаться завершения правильного дочернего процесса?

ОБНОВЛЕНИЕ:

Вышеуказанное инициируется следующей командой:

const winston = require('../../server/winston');
const perform_vrp = require('../../server/modules/vrp');
const moment = require('moment');
const Utils = require('../modules/utils');
let utils = new Utils();

let do_vrp = async function(next_delivery_date, cities) {
  winston.info('Generating routes for %s', next_delivery_date);
  for (let index in cities) {
    if (cities.hasOwnProperty(index)) {
      let city = cities[index];
      winston.info('Generating route for %s on %s', city, next_delivery_date);
      await perform_vrp(next_delivery_date,[city]);
      await utils.sleep(60000);
    }
  }
};


let process_routes = async function() {

  //...

  let morning_cities = ['Boston','Chicago'];
  await do_vrp(next_delivery_date.format('YYYY-MM-DD'), morning_cities);
};

process_routes();

1 Ответ

0 голосов
/ 27 октября 2018

Проблема в том, что вы используете await внутри цикла for для ожидания завершения сценария, но не переносите выполнение сценария в Promise .

In execute_tsp

деформирует спавн и событие в Обещании, которое разрешается, когда событие data срабатывает, и разрешается с помощью данных, полученных следующим образом:

  let perform_tsp = function () {
  //..
  return new Promise((resolve, reject) => {
    const pyProg = spawn('python3', [process.env.PWD + '/server/vrp_solver/tsp_solver.py', '/../../route_data/' + depot.city + '/' + moment(route.date_driven).format('Y-MM-DD'), 'morning', route.name]);
    winston.info('Solving the TSP for %s...', route.name);
    pyProg.stdout.on('data', function (data) {
      resolve(data);
    })
  });
}

В execute_vrp

Просто верните возвращенное обещание от execute_tsp, чтобы вы могли ожидать его в цикле for:

let perform_vrp = function () {
  //..
  return perform_tsp();
}

В do_vrp

Получите данные разрешенного обещания, которые являются данными spawn, и после этого вы можете выполнить логику, которая была в обратном вызове события data :

 let do_vrp = async function (next_delivery_date, cities) {
  winston.info('Generating routes for %s', next_delivery_date);
  for (let index in cities) {
    if (cities.hasOwnProperty(index)) {
      let city = cities[index];
      winston.info('Generating route for %s on %s', city, next_delivery_date);
      let data = await perform_vrp(next_delivery_date, [city]);
      let result_string = data.toString();
      winston.info('Route result for %s is: %s', route.name, result_string);
      let result_array = eval(result_string);
      await utils.sleep(60000);
    }
  }
};

Предыдущая структура будет запускать спавны последовательно, если вы хотите повторно запустить спавн при параллельном использовании Promise.all api.

  let do_vrp = async function (next_delivery_date, cities) {
  winston.info('Generating routes for %s', next_delivery_date);

  const promises = cities.map((city) => {
    return perform_vrp(next_delivery_date, [city]);
  });

  const results = await Promise.all(promises);
  results.forEach((data) => {
    winston.info('Generating route for %s on %s', city, next_delivery_date);
    let result_string = data.toString();
    winston.info('Route result for %s is: %s', route.name, result_string);
    let result_array = eval(result_string);
  });
};
...