Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Catch the status of a child process as a stream error #31

Open
tyrak opened this issue Jul 9, 2019 · 1 comment
Open

Catch the status of a child process as a stream error #31

tyrak opened this issue Jul 9, 2019 · 1 comment

Comments

@tyrak
Copy link

tyrak commented Jul 9, 2019

I am using duplexify to create a duplex stream out of a child process. This enables me to create complex pipelines, combining processes with other streams. So, I wrote a simple function that wraps duplexify and child_process.spawn as follows:

function run(program, args) {
	const child = spawn(program, args, {
		stdio: ["pipe", "pipe", "inherit"],
	});
	const stream = duplexify(child.stdin, child.stdout);
	child.on("error", (err) => {
		stream.destroy(err);
	});
	return stream;
}

Now I can use the stream returned by run with stream.pipeline. The only issue that I have with this solution is that I would like run to wait for the child processes' exit status, and if it is not zero, forward an error to the duplex stream. I tried to address this by adding a final method as follows:

function run(program, args) {
	const child = spawn(program, args, {
		stdio: ["pipe", "pipe", "inherit"],
	});
	const final = (stream, callback) => {
		child.on("exit", (code, signal) => {
			if (code !== 0)
				callback(new Error("child process failed"));
			else
				callback();
		});
	};
	const stream = duplexify(child.stdin, child.stdout, {final});
	child.on("error", (err) => {
		stream.destroy(err);
	});
	return stream;
}

but the final function never gets called by duplexify. Is there a way to solve this issue with duplexify?

@istarkov
Copy link

istarkov commented May 25, 2021

Modern days you can use async iterables instead of duplexify

I use spawn wrapper like

  const someCmd = (cmd:string) => {
    // eslint-disable-next-line @typescript-eslint/no-unused-vars
    let resolve_ = (_value?: unknown) => {
      //
    };

    // eslint-disable-next-line @typescript-eslint/no-unused-vars
    let reject_ = (_e: Error) => {
      //
    };

    const pWait = new Promise((r, rj) => {
      resolve_ = r;
      reject_ = rj;
    });

    const psql = spawn(execCmd, [...execArgs, ...commonArgs, '-c', cmd], {
      stdio: ['pipe', 'pipe', process.stderr],
    });

    psql.on('exit', code => {
      if (code != null && code > 0) {
        // throw new Error('process exited with error');
        console.error('EXIT CODE');
        reject_(new Error('process exited with non null code'));
      }
      resolve_();
    });

    // return stream;
    return async function* (source: NodeJS.ReadableStream) {
      if (source != null) {
        source.setEncoding('utf8');
        pipeline(source, psql.stdin, err => {
          if (err != null) {
            reject_(err);
          }
        });
      }

      for await (const chunk of psql.stdout) {
        yield chunk;
      }

      await pWait;
    } as never;
  };

Then

try {
await pipeline(
  eee(),
  someCmd(),
  blabla()
)
} catch(e) {
  <--- In case of spawn returns error code will be here
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants