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

Node.js中如何正确重抛http req.on('error')回调内的错误?

问题:如何捕获HTTP请求回调中的错误并重抛?

我正在AKS集群中配置一个Kubernetes Job,启动后执行uploadRepairCatalog() JavaScript函数,通过端点上传维修目录到服务器。

核心流程逻辑

  • uploadRepairCatalog()的try块:先调用createFirebaseUser()生成服务器所需的authToken,再调用sendRepairData()循环发起HTTP请求,两个函数均可能抛出错误
  • catch块:若上述函数报错,调用deleteRepairs()清理sendRepairData()已存入数据库的数据,随后重抛错误触发容器重启
  • finally块:调用deleteFirebaseUser()清理初始创建的Firebase用户

遇到的问题

本地测试时,sendRepairData()因无法连接服务器报错,但此时deleteFirebaseUser()并未执行;重新启动代码后,createFirebaseUser()因用户已存在报错,finally块却能正常执行。

调试后确认:sendRepairData()中req.on('error', (error) => {})回调内抛出的错误,无法被sendRepairData()的catch块捕获,因此无法传递到uploadRepairCatalog()的catch块;但直接抛出普通Error时,流程能正常执行。

相关代码

async function sendRepairData() {
  // Read the catalog JSON file
  try {
    const catalogData = fs.readFileSync('catalog.json'); 
    const catalog = JSON.parse(catalogData);

    // Access the repairs array from the catalog
    repairs = catalog.repairs;
    console.log('expected repairs to send: ', repairs.length);

    for (const repair of repairs) {
      const options = {
        host: host,
        port: port,
        path: path,
        method: 'POST',
        headers: {
          'Content-Type': 'application/json',
          AuthToken: authToken,
          apikey: apiKey,
        },
      };

      const req = http.request(options, (res) => {
        let data = '';

        res.on('data', (chunk) => {
          data += chunk;
        });

        res.on('end', () => {
          console.log('Response:', data);
          // save response data for resetting the db in case of error
          savedRepairs.push(JSON.parse(data));
        });
      });

      req.on('error', (error) => {
        console.log('sendRepairData() http request error:', error);
        throw error; // 此处错误无法被外层catch捕获
        // throw Error(`${error.name}, ${error.message}, ${error.stack}`);
      });

      req.write(JSON.stringify(repair));
      req.end();
      // throw Error('test error from sendRepairData()'); // 直接抛出错误能被捕获
    } 
  } catch (error) {
    console.error('\n\nsendRepairData() catch\n\nerror :', error);
    throw error;
  } 
}

解决方案

核心原因

Node.js的事件回调(如req.on('error'))属于异步任务,抛出的错误不会冒泡到当前的try/catch块——因为try/catch仅能捕获同步代码或通过await等待的Promise异步代码中的错误。回调函数的执行上下文脱离了原try块的作用域,所以直接throw的错误无法被捕获。

解决方法:将HTTP请求封装为Promise

把http.request的回调逻辑包装成Promise,通过resolve和reject管理异步状态,再用await等待每个请求完成,这样错误就能被外层的try/catch捕获。

修改后的代码

async function sendRepairData() {
  // Read the catalog JSON file
  try {
    const catalogData = fs.readFileSync('catalog.json'); 
    const catalog = JSON.parse(catalogData);

    // Access the repairs array from the catalog
    repairs = catalog.repairs;
    console.log('expected repairs to send: ', repairs.length);

    // 用await确保每个请求完成后再处理下一个
    for (const repair of repairs) {
      await new Promise((resolve, reject) => {
        const options = {
          host: host,
          port: port,
          path: path,
          method: 'POST',
          headers: {
            'Content-Type': 'application/json',
            AuthToken: authToken,
            apikey: apiKey,
          },
        };

        const req = http.request(options, (res) => {
          let data = '';

          res.on('data', (chunk) => {
            data += chunk;
          });

          res.on('end', () => {
            console.log('Response:', data);
            savedRepairs.push(JSON.parse(data));
            resolve(); // 请求成功,标记Promise完成
          });
        });

        req.on('error', (error) => {
          console.log('sendRepairData() http request error:', error);
          reject(error); // 将错误传递给Promise的reject
        });

        req.write(JSON.stringify(repair));
        req.end();
      });
    } 
  } catch (error) {
    console.error('\n\nsendRepairData() catch\n\nerror :', error);
    throw error;
  } 
}

关键修改说明

  1. Promise封装:用new Promise()包裹每个http.request的逻辑,请求成功时调用resolve(),错误时调用reject(error),将异步操作转为Promise链
  2. await等待:在for循环中用await等待每个Promise完成,确保异步错误能被外层try/catch捕获
  3. 错误传递:回调中不再直接throw错误,而是通过reject(error)将错误传递到Promise链,最终被sendRepairData()的catch块捕获并重抛

修改后,req.on('error')中的错误会正常触发sendRepairData()的catch块,进而传递到uploadRepairCatalog()的catch块执行清理逻辑,finally块也会正常执行deleteFirebaseUser()。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.19 23:27:04