Как избежать async / Promise antipattern с помощью `childProcess.spawn`? - PullRequest
0 голосов
/ 20 апреля 2020

Я пишу код node.js (v10 +) в машинописи (3.8.3+). Я хочу обернуть childProcess.spawn() для моего случая использования.

Пока у меня есть что-то подобное, которое работает, но я знаю, что создание явных обещаний в асинхронной функции c обычно неправильно.

// 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)
  })

Цели:

  • Сделать это ожидаемым
  • Разрешить асинхронный c onStart() обратный вызов
  • Сделать это, когда подпроцесс завершен (при 'закрытии')

Так что для меня это выглядит подозрительно как антипаттерн создания нового Promise в asyn c функции. Но я не вижу, как избавиться от явного Promise при сохранении всех моих целей. Мне нужно, чтобы он разрешил что-то , когда подпроцесс завершен, и мне нужно иметь возможность ждать onStart перед возвратом, поэтому я не могу удалить "asyn c" верхнего уровня.

Обратите внимание, что onStart должен работать, как только подпроцесс c создан и обработчики на месте - не ждите, пока про c завершится, отсюда и ожидание.

1 Ответ

0 голосов
/ 21 апреля 2020

Вместо async на runSubprocess вы можете поместить его в анонимную функцию в конструкторе Promise:

// 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, используйте для этого библиотеку.

...