You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

回调函数多次触发问题排查:Node.js子进程监听逻辑异常

问题原因与解决方案

我一眼就发现了问题所在:你的data事件回调可能被多次触发,因为Node.js会将子进程的标准输出拆分成多个数据块(chunk)发送,即使子进程只输出了一次日志。

当第一个数据块到达时,可能还没包含完整的匹配字符串,所以不会触发回调;当后续数据块到达并拼接后,字符串满足匹配条件,此时你调用了n.stdout.removeAllListeners(),但如果已经有多个data事件的回调任务被放入事件队列,那么已经排队的回调仍然会执行——而此时stdout已经包含了匹配的字符串,所以每次排队的回调都会进入if分支,反复执行你的cb和console.log(33333)。

举个实际场景:子进程输出的完整日志是live-mutex broker is listening on port 8019.,Node.js可能把它拆成两个数据块:

  1. 第一个块:live-mutex broker is listen(不匹配正则)
  2. 第二个块:ing on port 8019.(拼接后满足匹配条件,执行回调)
  3. 如果还有第三个块(比如单独的换行符\n),此时stdout已经包含匹配字符串,即使你已经移除了监听,但如果这个回调已经在队列里,还是会触发。

修复方案

添加一个执行标志,确保回调只会被调用一次:

export const launchBrokerInChildProcess = function (opts: any, cb: Function) { 
  const host = opts.host || 'localhost'; 
  const port = opts.port || 8019; 
  const detached = Boolean(opts.detached); 
  ping.probe(host, port, function (err, available) { 
    if (err) { 
      return cb(err) 
    } 
    if (available) { 
      log.info(`live-mutex broker/server was already live at ${host}:${port}.`); 
      return cb(null, {host, port, alreadyRunning: true}); 
    } 
    log.info(`live-mutex is launching new broker at '${host}:${port}'.`); 
    const n = cp.spawn('node', [p], { 
      detached, 
      env: Object.assign({}, process.env, { LIVE_MUTEX_PORT: port }) 
    }); 
    if (detached) { 
      n.unref(); 
    } 
    process.once('exit', function () { 
      if (!detached) { 
        n.kill('SIGINT'); 
      } 
    }); 
    n.stderr.setEncoding('utf8'); 
    n.stdout.setEncoding('utf8'); 
    n.stderr.pipe(process.stderr); 
    let stdout = '';
    let callbackInvoked = false; // 新增:标记回调是否已执行
    n.stdout.on('data', function (d) { 
      if (callbackInvoked) return; // 已执行过回调,直接返回
      stdout += String(d); 
      if (stdout.match(/live-mutex broker is listening/i)) { 
        callbackInvoked = true; // 设置标记,防止重复执行
        n.stdout.removeAllListeners(); 
        // 下面的n.unref()是冗余代码,之前已经调用过了,可以删除
        // if (detached) { n.unref(); }
        console.log(33333); 
        cb(null, { liveMutexProcess: n, host, port, detached }); 
      } 
    }); 
  }); 
};

这个标志会在第一次触发回调时被设置为true,后续所有data事件的回调都会直接跳过,彻底避免重复执行。另外,你代码里在匹配到字符串后再次调用n.unref()是多余的,因为在spawn之后已经针对detached情况调用过一次了,可以删掉这部分冗余代码。

内容的提问来源于stack exchange,提问作者Alexander Mills

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.29 07:47:10