如何将 Child_process.spawn 的“Promise”语法转换为“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)):
                });
            }
        });
    })
}

我有一些我想在 tools/__main__.py 中执行的 python 代码,所以这就是我调用“pipenv”的原因。

这是我尝试以 异步/等待 方式编写它(实际上有效):

 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... 循环的情况下使用 异步/等待 方式编写此代码?

我也不知道如何访问数据,除了使用 .then 语法:

 callToolsAsync(['GET','mailuser'])
.then(console.log)

我还能如何从 resolve(data) 访问“数据”?

非常感谢。

原文由 tinnick 发布,翻译遵循 CC BY-SA 4.0 许可协议

阅读 1k
2 个回答

使用 async/await 编写代码可能没有比在 Node.js 中使用 for async (chunk of stream) 语法 更好的 方法了。节点流专门实现了一个异步迭代器来允许这样做。

这篇 关于2ality的文章 有更深入的解释和讨论。关于 Symbol.asyncIteratorfor-await…of 的 MDN 文章更普遍地涵盖了异步迭代。

一旦决定使用异步迭代,就必须在 async 函数中使用它,它将返回一个承诺。

虽然在返回的承诺上使用 then 子句是获取数据的完全正常方式,但您也可以 await 结果 callToolsAsync(req) 4 -a344该调用在 async 函数中编码,以便 await 处于有效上下文中。


以下代码 实验 获取 stdiostderr 输出,以及子进程的退出代码。它不使用 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);}
);

孩子.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 将等待当时没有可用数据的开放流。
  • 从管道中检索 stdoutstderr 内容,并获取退出代码可以不按特定顺序完成,因为检索是异步的。
  • 合并通过管道从另一个进程到达的数据块 不是可选的——来自子进程的控制台日志是单独通过的。

原文由 traktor 发布,翻译遵循 CC BY-SA 4.0 许可协议

重要的是要了解 async/await 和 promises 是 _一样的_,只是语法不同。

所以 每个异步函数都会返回一个 promise

所以假设你有一个函数返回一个承诺:

 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 功能 不是 承诺风格。使用承诺时,您 永远不会 调用 new Promise 。基本上 new Promise 的整个想法是将异步非承诺代码(因此非异步/等待相同)转换为承诺(以及异步/等待)。

现在 异步 并不意味着 async/await 而是一个更笼统的概念。处理异步的另一种常见方法是回调。

所以 tool.on('exit', (code) => { 是 _异步的_,但既不是 Promise 也不是异步/等待。

所以包装 new Promise 基本上用于将其转换为可用作 Promise 或与异步/等待一起使用的 Promise 样式函数。


关于这个片段的最后一句话:

 for await (const data of tool.stdout) {
    return data
}

这有点问题。虽然节点流是异步生成器,但它会在第一个块之后返回,并在流接收到多个块后立即中断。因此,您应该将 return 替换为 yield 并返回一个异步生成器,或者在循环完成后将缓冲区与循环和 return 连接起来。

原文由 Lux 发布,翻译遵循 CC BY-SA 4.0 许可协议

撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
推荐问题
logo
Stack Overflow 翻译
子站问答
访问
宣传栏