#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'))