如何将 Child_process.spawn 的 "Promise" 语法转换为 "async/await" 语法
how to turn Child_process.spawn's "Promise" syntax to "async/await" syntax
所以我有这段代码,我正在尝试全面深入地理解 async/await 语法。以下是 Promise 版本的代码:
function callToolsPromise(req) {
return new Promise((resolve, reject) => {
let pipshell = 'pipenv';
let args = ['run', 'tools'];
req.forEach(arg => {
args.push(arg)
});
tool = spawn(pipshell, args);
tool.on('exit', (code) => {
if (code !== 0) {
tool.stderr.on('data', (data) => {
reject(data);
});
} else {
tool.stdout.on ('data', (data) => {
resolve(JSON.parse(data)):
});
}
});
})
}
我有一些 python 代码要在 tools/__main__.py 中执行,所以这就是我调用 "pipenv".
的原因
这是我尝试以 async/await 方式编写的(实际有效):
async function callToolsAsync(req) {
let pipshell = 'pipenv';
let args = ['run', 'tools'];
req.forEach(arg => {
args.push(arg)
});
let tool = spawn(pipshell, args);
for await (const data of tool.stdout) {
return data
}
}
但我所做的只是从某人的示例中复制并粘贴我有 for await...
循环的地方。
因此,我一直在尝试重写相同的代码,以便我能够真正理解它,但我已经失败了好几天了。
有没有其他方法可以在不使用 for await...
循环的情况下用 async/await 方式编写此代码?
此外,除了使用 .then
语法外,我不知道如何访问数据:
callToolsAsync(['GET','mailuser'])
.then(console.log)
我还能如何从 resolve(data)
访问 "data"?
非常感谢。
重要的是要了解 async/await
和 promises 相同,只是语法不同。
所以每个异步函数return都是一个承诺!
所以假设你有一个返回承诺的函数:
function foo() {
return new Promise(resolve => setTimeout(() => resolve("done"), 1000));
}
有两种消费方式。
承诺风格:
function test() {
foo().then(value => console.log(value));
}
或异步等待:
async function test() {
const value = await foo();
console.log(value);
}
现在重要的是要了解您的原始 callToolsPromise
函数是 而不是 promise 样式。使用 promises 时,您 永远不会 调用 new Promise
。基本上 new Promise
的整个想法是将异步非承诺代码(因此 non-async/await 相同)转换为 Promises(因此 async/await)。
现在异步并不是说async/await
而是一个更笼统的概念。处理异步的另一种常见方法是回调。
所以 tool.on('exit', (code) => {
是 异步的 ,但既不是 Promise 也不是 async/await.
所以包装 new Promise
基本上用于将其转换为 Promise 样式函数,可以用作 Promise 或与 async/await.
关于这个片段的最后一句话:
for await (const data of tool.stdout) {
return data
}
这有点问题。虽然节点流是异步生成器,这将在第一个块之后 return 并在流接收到多个块后立即中断。
因此,您应该将 return
替换为 yield
和 return 一个异步生成器,或者在循环完成后将缓冲区与循环和 return
连接起来。
使用 async/await
编写代码可能没有 更好 的方法,而不是在 Node.js 中使用 for async (chunk of stream)
语法。节点流实现了一个专门用于允许这样做的异步迭代器。
这个 article on 2ality has more in-depth explanation and discussion. MDN articles on Symbol.asyncIterator and for-await...of 涵盖了更普遍的异步迭代。
一旦决定使用异步迭代,它就必须在 async
函数中使用,这将 return 一个承诺。
虽然在 returned promise 上使用 then
子句是获取数据的一种完全正常的方式,但您也可以 await
callToolsAsync(req)
的结果 -当然前提是调用是在 async
函数中编码的,以便 await
在有效的上下文中。
以下代码 experiment 获取 stdio
和 stderr
输出,以及来自子进程的退出代码。它不使用 Python 或解析数据。
main.js(输入node main.js
到运行)
// main.js
async function spawnChild() {
const { spawn } = require('child_process');
const child = spawn('node', ["child.js"]);
let data = "";
for await (const chunk of child.stdout) {
console.log('stdout chunk: '+chunk);
data += chunk;
}
let error = "";
for await (const chunk of child.stderr) {
console.error('stderr chunk: '+chunk);
error += chunk;
}
const exitCode = await new Promise( (resolve, reject) => {
child.on('close', resolve);
});
if( exitCode) {
throw new Error( `subprocess error exit ${exitCode}, ${error}`);
}
return data;
}
spawnChild().then(
data=> {console.log("async result:\n" + data);},
err=> {console.error("async error:\n" + err);}
);
child.js
// child.js
console.log( "child.js started"); // stdout
setTimeout( finish, 1000);
function finish() {
console.log( "child.js: finish() call"); // stdout
console.error("child exit using code 1"); // stderr
process.exit(1);
}
这表明
- 控制台警告可读流的异步迭代在节点中仍处于试验阶段,
for await (chunk of stream)
循环似乎一直循环到流关闭 - 在这种情况下意味着 await
将等待当时没有可用数据的打开流。
- 从他们的管道中检索
stdout
和 stderr
内容,并且获取退出代码可以不按特定顺序完成,因为检索是异步的。
- 合并从另一个进程通过管道到达的数据块不是可选的 - 来自子进程的控制台日志是单独通过的。
我使用 await-spawn
解决了这个问题
https://www.npmjs.com/package/await-spawn
child_process.spawn() wrapped in a Promise for doing async/await.
所以我有这段代码,我正在尝试全面深入地理解 async/await 语法。以下是 Promise 版本的代码:
function callToolsPromise(req) {
return new Promise((resolve, reject) => {
let pipshell = 'pipenv';
let args = ['run', 'tools'];
req.forEach(arg => {
args.push(arg)
});
tool = spawn(pipshell, args);
tool.on('exit', (code) => {
if (code !== 0) {
tool.stderr.on('data', (data) => {
reject(data);
});
} else {
tool.stdout.on ('data', (data) => {
resolve(JSON.parse(data)):
});
}
});
})
}
我有一些 python 代码要在 tools/__main__.py 中执行,所以这就是我调用 "pipenv".
的原因这是我尝试以 async/await 方式编写的(实际有效):
async function callToolsAsync(req) {
let pipshell = 'pipenv';
let args = ['run', 'tools'];
req.forEach(arg => {
args.push(arg)
});
let tool = spawn(pipshell, args);
for await (const data of tool.stdout) {
return data
}
}
但我所做的只是从某人的示例中复制并粘贴我有 for await...
循环的地方。
因此,我一直在尝试重写相同的代码,以便我能够真正理解它,但我已经失败了好几天了。
有没有其他方法可以在不使用 for await...
循环的情况下用 async/await 方式编写此代码?
此外,除了使用 .then
语法外,我不知道如何访问数据:
callToolsAsync(['GET','mailuser'])
.then(console.log)
我还能如何从 resolve(data)
访问 "data"?
非常感谢。
重要的是要了解 async/await
和 promises 相同,只是语法不同。
所以每个异步函数return都是一个承诺!
所以假设你有一个返回承诺的函数:
function foo() {
return new Promise(resolve => setTimeout(() => resolve("done"), 1000));
}
有两种消费方式。
承诺风格:
function test() {
foo().then(value => console.log(value));
}
或异步等待:
async function test() {
const value = await foo();
console.log(value);
}
现在重要的是要了解您的原始 callToolsPromise
函数是 而不是 promise 样式。使用 promises 时,您 永远不会 调用 new Promise
。基本上 new Promise
的整个想法是将异步非承诺代码(因此 non-async/await 相同)转换为 Promises(因此 async/await)。
现在异步并不是说async/await
而是一个更笼统的概念。处理异步的另一种常见方法是回调。
所以 tool.on('exit', (code) => {
是 异步的 ,但既不是 Promise 也不是 async/await.
所以包装 new Promise
基本上用于将其转换为 Promise 样式函数,可以用作 Promise 或与 async/await.
关于这个片段的最后一句话:
for await (const data of tool.stdout) {
return data
}
这有点问题。虽然节点流是异步生成器,这将在第一个块之后 return 并在流接收到多个块后立即中断。
因此,您应该将 return
替换为 yield
和 return 一个异步生成器,或者在循环完成后将缓冲区与循环和 return
连接起来。
使用 async/await
编写代码可能没有 更好 的方法,而不是在 Node.js 中使用 for async (chunk of stream)
语法。节点流实现了一个专门用于允许这样做的异步迭代器。
这个 article on 2ality has more in-depth explanation and discussion. MDN articles on Symbol.asyncIterator and for-await...of 涵盖了更普遍的异步迭代。
一旦决定使用异步迭代,它就必须在 async
函数中使用,这将 return 一个承诺。
虽然在 returned promise 上使用 then
子句是获取数据的一种完全正常的方式,但您也可以 await
callToolsAsync(req)
的结果 -当然前提是调用是在 async
函数中编码的,以便 await
在有效的上下文中。
以下代码 experiment 获取
stdio
和 stderr
输出,以及来自子进程的退出代码。它不使用 Python 或解析数据。
main.js(输入node main.js
到运行)
// main.js
async function spawnChild() {
const { spawn } = require('child_process');
const child = spawn('node', ["child.js"]);
let data = "";
for await (const chunk of child.stdout) {
console.log('stdout chunk: '+chunk);
data += chunk;
}
let error = "";
for await (const chunk of child.stderr) {
console.error('stderr chunk: '+chunk);
error += chunk;
}
const exitCode = await new Promise( (resolve, reject) => {
child.on('close', resolve);
});
if( exitCode) {
throw new Error( `subprocess error exit ${exitCode}, ${error}`);
}
return data;
}
spawnChild().then(
data=> {console.log("async result:\n" + data);},
err=> {console.error("async error:\n" + err);}
);
child.js
// child.js
console.log( "child.js started"); // stdout
setTimeout( finish, 1000);
function finish() {
console.log( "child.js: finish() call"); // stdout
console.error("child exit using code 1"); // stderr
process.exit(1);
}
这表明
- 控制台警告可读流的异步迭代在节点中仍处于试验阶段,
for await (chunk of stream)
循环似乎一直循环到流关闭 - 在这种情况下意味着await
将等待当时没有可用数据的打开流。- 从他们的管道中检索
stdout
和stderr
内容,并且获取退出代码可以不按特定顺序完成,因为检索是异步的。 - 合并从另一个进程通过管道到达的数据块不是可选的 - 来自子进程的控制台日志是单独通过的。
我使用 await-spawn
https://www.npmjs.com/package/await-spawn
child_process.spawn() wrapped in a Promise for doing async/await.