【问题标题】:In node.js, how to use child_process.exec so all can happen asynchronously?在 node.js 中,如何使用 child_process.exec 让一切都可以异步发生?
【发布时间】:2014-05-04 11:45:59
【问题描述】:

我有一个基于 node.js 的服务器。以下是请求处理函数之一:

var exec = require("child_process").exec

function doIt(response) {

    //some trivial and fast code - can be ignored

    exec(
        "sleep 10",  //run OS' sleep command, sleep for 10 seconds
        //sleeping(10), //commented out. run a local function, defined below.
        function(error, stdout, stderr) {
            response.writeHead(200, {"Content-Type": "text/plain"});
            response.write(stdout);
            response.end();
    });

    //some trivial and fast code - can be ignored
}

同时,在同一个模块文件中定义了一个本地函数“sleeping”,顾名思义,它将休眠 10 秒。

function sleeping(sec) {
    var begin = new Date().getTime();
    while (new Date().getTime() < begin + sec*1000); //just loop till timeup.
}

这里有三个问题--

  1. 众所周知,node.js 是单处理、异步、事件驱动的。是否所有带有回调参数的函数都是异步的?例如,如果我有一个函数 my_func(callback_func),它将另一个函数作为参数。对 callback_func 或使 my_func 异步的地方有什么限制吗?

  2. 所以至少 child_process.exec 是异步的,以回调匿名函数作为参数。在这里,我将“sleep 10”作为第一个参数传递,以调用操作系统的睡眠命令并等待 10 秒。它不会阻塞整个节点进程,即发送到另一个请求处理程序的任何其他请求都不会被“doIt”处理程序阻塞 10 秒。但是,如果立即向服务器发送另一个请求并且应该由同一个“doIt”处理程序处理,它是否必须等到前一个“doIt”请求结束?

  3. 如果我使用 sleep(10) 函数调用(已注释掉)来替换“sleep 10”,我发现它会阻止其他请求直到 10 秒后。谁能解释一下为什么会有差异?

非常感谢!

-- 根据请求更新--

一条评论说,这个问题似乎与另一个问题 (How to promisify Node's child_process.exec and child_process.execFile functions with Bluebird?) 重复,后者是在这个问题一年后提出的。嗯,这些问题太不同了——一般来说,这是针对特定错误案例的异步问题提出的,而那个问题是在询问 Promise 对象本身。意图和用例都各不相同。

(如果有任何机会这些相似,不应该将新的标记为与旧的重复吗?)

【问题讨论】:

  • 天哪,你能看看发布问题时的时间戳吗?这是在一年前提出的。
  • 我不认为哪个问题有更多的流量(由统计数据得出结论 - 投票、观点等)重要,因此更有可能在网络搜索中找到。想想人:)
  • 好吧,这是有道理的。你介意告诉我最常见的部分吗?我已经很久没有访问这个问题了,它们看起来完全不同。如果它们确实不同,也许我可以更好地澄清。

标签: javascript node.js asynchronous child-process


【解决方案1】:

首先你可以promisify child_process。

const util = require('util');
const exec = util.promisify(require('child_process').exec);

async function lsExample() {
  const { stdout, stderr } = await exec('ls');
  if (stderr) {
    // handle error
    console.log('stderr:', stderr);
  }
  console.log('stdout:', stdout);

}
lsExample()

作为一个异步函数,lsExample 返回一个承诺。

Promise.all([]) 并行运行所有承诺。

Promise.all([lsExample(), otherFunctionExample()]);

如果您需要等待 Promise 并行完成,请等待它们。

await Promise.all([aPromise(), bPromise()]);

如果你需要这些承诺的价值

const [a, b] = await Promise.all([aPromise(), bPromise(])

【讨论】:

    【解决方案2】:

    1) 否。例如.forEach 是同步的:

    var lst = [1,2,3];
    console.log("start")
    lst.forEach(function(el) {
        console.log(el);
    });
    console.log("end")
    

    函数是否异步完全取决于实现 - 没有限制。您无法先验地知道它(您必须对其进行测试或知道它是如何实现的或阅读并相信文档)。还有更多,取决于参数,函数可以是异步的或同步的,或两者兼而有之。

    2) 不会。每个请求都会产生一个单独的“睡眠”进程。

    3) 那是因为你的sleeping 函数完全是一团糟——它根本不是睡眠。它的作用是使用无限循环并检查日期(因此使用 100% 的 CPU)。由于 node.js 是单线程的,所以它只会阻塞整个服务器——因为它是同步的。这是错误的,不要这样做。请改用setTimeout

    【讨论】:

    • 我喜欢你的回答。对于(2),所以如果我理解正确, exec 实际上是基于第一个参数的新进程?或者它只是一个新线程?对于 (3),使用 setTimeout 本身似乎是异步的。有没有办法测试同步本地功能?
    • @Bruce (2) 是的,exec 产生一个新的子进程(对于当前的 node.js 进程)。 node.js 中没有线程。 (3)我不确定我是否理解这个问题:“就地测试”是什么意思?无论如何,在 Node.js 中没有办法进行同步睡眠。
    • 非常感谢,太奇怪了。我认为你的回答已经足够好了。在接受之前我会稍等片刻,看看更多的cmets。
    • 抱歉,最后一个还是有点乱。如果我将 exec 的第一个参数更改为“find /”并设置 {timeout:20000, maxBuffer:20000*1024},则生成的新进程仍将占用 100% CPU,但是,它不会阻止发送的其他请求和异步处理。根据操作系统,新生成的进程无论如何都是一个进程,因此应该由操作系统来安排运行哪个内核。即使它本身占用了该内核 100% 的 CPU,它也不应该影响其他进程,就像产生它的那个进程一样,对吧?
    • @Bruce 没错。我说的是sleeping 函数,而不是sleep 10 子进程。请注意,如果您使用sleeping(10),则不会产生新进程。在您调用sleeping(10) 然后将结果(即undefined)传递给exec 调用的意义上,代码是错误的。因此它等待10 秒(阻塞整个服务器)和调用execundefined(我认为应该失败,即抛出异常?)。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2013-07-28
    • 1970-01-01
    • 1970-01-01
    • 2021-12-31
    • 2021-05-25
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多