Node.js:当子进程在对其标准输入流进行大量写入操作期间突然关闭时,会引发无法捕获的错误

标签 node.js stdin child-process node-streams epipe

注意:我已经找到了这个问题的解决方案,将其发布在这里供后代使用。查看所选答案。


以下(简化的)代码会引发无法捕获的“写入 EPIPE”(在某些情况下“写入 EOF”)错误:

const { exec } = require("child_process");

const veryLargeString = "x".repeat(10 * 1024 * 1024);

const p = exec("gibberishThatWillFailImmediately");

p.stdin.write(veryLargeString);

我对这个问题的失败尝试:

  • 写入前检查 stdin.destroyed 标志
  • 写入前检查 stdin.writeableEnded 标志
  • 对输入进行分块并在每个 block 之前检查 stdin.writeableEnded 。这会导致不确定的行为。
  • 使用 try-catch 包装 stdin.write(data)
  • 调用 stdin.end(data) 而不是 stdin.write(data)
  • 传递 stdin.write() 一个回调,该回调应该获取发生的任何错误。回调收到了错误,但并没有阻止它被抛出。

最佳答案

将“错误”处理程序注册到 stdin 流似乎可以防止抛出错误。像这样:

const { exec } = require("child_process");

const veryLargeString = "x".repeat(10 * 1024 * 1024);

const p = exec("gibberishThatWillFailImmediately");

p.stdin.on('error', (error) => console.log("error caught: ", error));

p.stdin.write(veryLargeString);

下面是一个返回包含错误的 Promise 的示例,如果没有发生错误,则返回 null:

const { exec } = require("child_process");

const veryLargeString = "x".repeat(10 * 1024 * 1024);

function safelyWriteDataToStdin(stdin, data) {
  // Register an awaitable callback that will capture any error occuring during the write operation
  const promise = new Promise((resolve, _reject) => {
    // Using once() and not on() to remove the listener after the first catch.
    stdin.once("error", (error) => resolve(error));

    // stdin.end(data, callback) can probably be used here, but I keep the `write()` just in case `end()`'s callback is called before the 'error' event, since the docs are not clear about that. (docs say: "The callback is invoked before 'finish' or on error." for node version 15.0.0. Is "on error" how node people say "after error"? idk.)
    stdin.write(
      data,
      (error) => {
        if (!error) resolve(null); // The condition is necessary because when an error occurs, the callback is called before the 'error' event handler
      } // Signal the promise to complete when the write operation is complete with no errors. I don't simply use this `error` parameter because the exception will still be thrown if I don't listen to the 'error' event, and the docs say: "If an error occurs, the callback may or may not be called with the error as its first argument. To reliably detect write errors, add a listener for the 'error' event.". Also, I tested it myself and got two different errors in this callback and in the 'error' event handler.
    );
  });

  return promise;
}

const p = exec("gibberishThatWillFailImmediately");

safelyWriteDataToStdin(p.stdin, veryLargeString).then((error)=>console.log("The error is:", error ));

关于Node.js:当子进程在对其标准输入流进行大量写入操作期间突然关闭时,会引发无法捕获的错误,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/67296866/

相关文章:

node.js - Jade 模板 if 语句

c - 将行存储到动态结构中

node.js - 如何使用NodeJS和child_process.exec以管理员身份运行程序?

node.js - NodeJS : How to read stdout and stderr of a child process spawned by an explicitly spawned process?

node.js - 如何在不实际发布到 NPM 的情况下测试 `npm publish` 结果?

session - 使用 MongoDB 将 session 保存为 BSON,而不是 Node.js 上的字符串

c - 为什么我在 Raspberry-Pi Jessie Lite 上启动时无法访问标准输入?

C++清除输入缓冲区(最后没有换行符)

node.js - Nodejs 在子进程中的输出中断

javascript - 如何处理与 MongoDB 和 node.js 的异步?