Узел js выполняет скрипт python один за другим

#node.js #reactjs #asynchronous #synchronous

Вопрос:

у меня есть приложение React, и я хочу использовать узел для выполнения нескольких сценариев ( может быть N сценариев ) при нажатии кнопки, один за другим. У меня также есть некоторое состояние для каждого сценария, поэтому, когда сценарий будет завершен, я хочу установить его состояние в «завершено».

Немного погуглив, я получил :

 function runScript(command, args, options, id, callback) {
  const child = childProcess.spawn(command, args, options);

  child.stdout.on('data', (data) => {
    console.log(`${data}`);
  });

  child.stderr.on('data', (data) => {
    console.log(`${data}`);
  });

  // callback function to change the state to "completed"
  child.on('close', () => callback(id, 'completed'));
}

const deployScripts = () => {
    loadedSteps.forEach((script) => {
      if (script.path.length) {
        const [command, args, options] = getParamBasedOnFile(script.path);
        if (command) {
          //set the script to "active" which means is running
          updateStepStatus(script.id, 'active');
          runScript(command, args, options, script.id, updateStepStatus);
        }
      }
    });
  }; 
 

Это работает, но, конечно, так как его асинхронность запускает все сценарии одновременно, а это не то, чего я хочу.

Затем я пошел и попытался сделать это синхронно

 function runScript(command, args, options) {
  const child = childProcess.spawnSync(command, args, options);

  console.log(child.stdout.toString());
  // callback(id, 'completed');  -> doesnt work
  console.log('exit run script function');
}

const deployScripts = () => {
    loadedSteps.forEach((script) => {
      if (script.path.length) {
        const [command, args, options] = getParamBasedOnFile(script.path);
        if (command) {
          updateStepStatus(script.id, 'active');
          runScript(command, args, options, script.id, updateStepStatus);
          updateStepStatus(script.id, 'completed');
        }
      }
    });
  };
 

Но проблема в том, что даже если он запускает сценарий 1 на 1 (я вижу вывод в консоли), оба состояния сценария настроены на «завершение» одновременно (когда я тестировал его, 1 скрипт python спал 5 секунд, а другой спал 10 секунд, поэтому первый должен быть настроен на завершение, затем через 5 секунд второй).

Не уверен, что лучший способ-это сделать что-то подобное.

Ответ №1:

Вы можете пообещать runScript и реализовать серию шагов как async функцию, таким образом, вы можете использовать await этот способ, чтобы дождаться завершения каждого шага перед выполнением следующего шага.

 const childProcess = require("child_process")

/* mocks */

const loadedSteps = [{path:'foo', id:1}, {path:'bar', id:2}, {path:'baz', id:3}]

function getParamBasedOnFile(path) {
    return ['python', ['-c', "import time;time.sleep(1)"], {}]
}

function updateStepStatus(id, status) {
    console.log(id, status)
}

/* code */

function runScript(command, args, options) {
    return new Promise((resolve, reject) => {
        const child = childProcess.spawn(command, args, options);
        child.on('close', () => resolve(child.stdout.toString()))
    })
}

async function deployScripts() {
    for(const script of loadedSteps) {
        if (script.path.length) {
            const [command, args, options] = getParamBasedOnFile(script.path);
            if (command) {
                updateStepStatus(script.id, 'active');
                await runScript(command, args, options, script.id);
                updateStepStatus(script.id, 'completed');
            }
        }
    }
};

/* main */

deployScripts().then(() => console.log('done'))