Welcome to OStack Knowledge Sharing Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
146 views
in Technique[技术] by (71.8m points)

node.js - Node - write child process spawn execution in class form

I'm re-writing an existing module which spawns a child process and executes a command.

I've re-written it as a class but when I run the code, I get an error that the Promise rejects and resolve are undefined. I assume that I pass them incorrectly to the .call method but I did not find a different way I can pass them.

Here's the code:

import logger from './logger.utils';
import { spawn, ChildProcess } from 'child_process';

/**
 * This function runs a spawn command and rejects the promise if timed out
 * @param cmd - the command to execute
 * @param params - the command's parameters
 * @param timeoutMs - timeout in milliseconds
 * @param taskDescription - a text description of the task for logging
 */
export class SpawnTimeout {
  cmd: string;
  params: string[];
  finished: boolean;
  childProcess: ChildProcess;
  timeoutMs: number;
  timeout: NodeJS.Timeout;
  taskDescription: string;
  handlers: Object;

  constructor(
    cmd: string,
    params: string[],
    timeoutMs: number,
    taskDescription: string = 'no description specified'
  ) {
    this.finished = false;
    this.childProcess = spawn(cmd, params, {
      stdio: [process.stdin, process.stdout, process.stderr],
    });
    this.timeoutMs = timeoutMs;
    this.timeout = null;
    this.taskDescription = taskDescription;
    this.cmd = cmd;
    this.params = params;
  }
  exec() {
    return new Promise((resolve, reject) => {
      const handlers = {
        resolve,
        reject,
      };
      this.handlers = handlers;
      this.childProcess.once('error', this._onError.call(this.handlers));
      this.childProcess.once('exit', this._onExit.call(this.handlers));
      this.timeout = setTimeout(this._setTimeout, this.timeoutMs);
    });
  }
  _onError(err: Error, handlers) {
    clearTimeout(this.timeout);
    const message = `spawn [${this.taskDescription}] ${this.cmd}, ${this.params} failed with error ${err}`;
    logger.error(message);
    handlers.reject(new Error(message));
  }

  _onExit(code: number, handlers) {
    this.finished = true;
    clearTimeout(this.timeout);
    logger.debug(`spawn [${this.taskDescription}] finished.code ${code}`);
    if (code == 0) {
      handlers.resolve(true);
    }
    // case of error, code !== 0
    const message = `spawn [${this.taskDescription}] cmd : ${this.cmd} ${this.params}. failed with code ${code}`;
    logger.error(message);
    handlers.reject(new Error(message));
  }

  _setTimeout() {
    if (!this.finished) {
      logger.warn(
        `spawn [${this.taskDescription}] - timeout. cmd : ${this.cmd}, ${this.params}`
      );
      this.childProcess.kill();
    }
  }
}

The error is generated when handlers.resolve or handlers.reject are called.

Please advise how can I resolve this? or even if such an implementation good practice.

question from:https://stackoverflow.com/questions/65936288/node-write-child-process-spawn-execution-in-class-form

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Answer

0 votes
by (71.8m points)

call immediately calls a function, the first parameter is this context with which a function is called, it doesn't return a function in this case and it's incorrect to provide the result as a listener for once.

Callback needs to be wrapped with a function to provide expected arguments:

this.childProcess.once('error', err => this._onError(err, this.handlers))
this.childProcess.once('exit', code => this._onExit(code, this.handlers));

Since callbacks are bound to correct this this way, it may be unnecessary to pass this.handlers to them as it's already available inside them.


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome to OStack Knowledge Sharing Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...