【问题标题】:How to avoid async/Promise antipattern with `childProcess.spawn`?如何使用`childProcess.spawn`避免异步/承诺反模式?
【发布时间】:2020-04-21 06:23:39
【问题描述】:

我正在使用 typescript (3.8.3+) 编写 node.js (v10+) 代码。我想为我的用例包装childProcess.spawn()

到目前为止,我有类似的东西,它正在工作,但我知道在异步函数中创建显式 Promise 通常是错误的。

// Returns a promise that resolves when the subprocess completes.
export async function runSubprocess(cmd: string, args: string[],
                                    onStdout?: (msg: string) => void)
                                    onStart?: (subproc) => Promise<void>) {
  // note: don't do anything async between creating the subproc and setting up
  // its event handlers, to avoid race conditions.
  return new Promise((resolve, reject) => {
    let stdout: string = ''
    let stderr: string = ''

    const subproc = childProcess.spawn(cmd, args)
    subproc.stdout.on('data', msg => {
      // accumulate all stdout into messages
      stdout += msg
      if (onStdout) { onStdout(msg) }
    })
    subproc.stderr.on('data', msg => { stderr += msg })
    subproc.on('error', e => {
      throw new SubprocessError(`Error starting ${cmd} ${args}`, e)
    })
    subproc.on('close', async (code, signal) => {
      if (code == 0) {
        resolve(0)
      } else {
        throw new SubprocessError(`Subproc ${cmd} ${args} returned error ${code} (signal: ${signal}\n STDOUT:\n${stdout}\n STDERR:\n${stderr}\n`)
      }
    })

    if (onStart)
      await onStart(subproc)
  })

目标是:

  • 让它可以等待
  • 允许异步 onStart() 回调
  • 在子流程完成后解决(在“关闭”时)

所以在我看来,这看起来像是在异步函数中创建新 Promise 的反模式。但我不知道如何在保持我所有目标的同时摆脱明确的Promise。当子进程完成后,我需要它来解决某事,并且我需要能够在返回之前等待 onStart,因此我无法删除顶级“异步”。

请注意,onStart 应该在创建子进程并且处理程序就位后立即运行 - 不等待 proc 完成,因此在那里等待。

【问题讨论】:

  • 实际上这里写的代码不起作用——我不能await onStart(),因为Promise 回调不是异步的。所以我有点卡住了。
  • child-process-promise 包可以为您完成繁重的工作,也许您可​​以使用它

标签: node.js typescript async-await es6-promise


【解决方案1】:

您可以将其放在 Promise 构造函数中的匿名函数上,而不是在 runSubprocess 上使用 async

// Returns a promise that resolves when the subprocess completes.
export function runSubprocess(cmd: string, args: string[],
                                    onStdout?: (msg: string) => void)
                                    onStart?: (subproc) => Promise<void>) {
  // note: don't do anything async between creating the subproc and setting up
  // its event handlers, to avoid race conditions.
  return new Promise(async (resolve, reject) => {
...

此外,您可能希望在收到错误时拒绝:

...
    subproc.on('error', e => {
      reject(new SubprocessError(`Error starting ${cmd} ${args}`, e))
    })
...

或者正如@CherryDT 建议的那样,使用一个库。

【讨论】:

  • 你的意思是new SubprocessError
猜你喜欢
  • 1970-01-01
  • 2023-03-30
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-02-05
  • 1970-01-01
  • 2015-04-02
  • 1970-01-01
相关资源
最近更新 更多