You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

NodeJS与Redis异步函数无法捕获Redis错误问题排查

我帮你排查下这个问题,咱们拆解来看:

核心问题分析

你遇到的Redis异常无法被捕获,根源在于redis@2.8.0的客户端行为:当Redis连接失败时,客户端会触发error事件,如果没有主动监听这个事件,Node.js会直接抛出未捕获的致命异常,根本轮不到Promise的catch块执行。另外你的测试代码里还有两处小问题,也影响了结果判断。

修复后的完整代码

1. 修正RedisManager类

const redis = require("redis");
const bluebird = require("bluebird");
const assert = require('assert');
bluebird.promisifyAll(redis.RedisClient.prototype);

class RedisManager {
  constructor(host) {
    this.client = redis.createClient({'host': host});
    
    // 关键:必须监听Redis的error事件,避免未捕获异常终止进程
    this.client.on('error', (err) => {
      console.error('Redis Client Error:', err);
    });

    // 新增:用Promise封装连接状态,确保后续操作能感知连接成败
    this.connectionPromise = new Promise((resolve, reject) => {
      this.client.on('ready', resolve);
      this.client.on('error', reject);
    });
  }

  async getKeys(key) {
    // 先等待连接状态确定,再执行Redis操作
    await this.connectionPromise;
    let result = await this.client.hgetallAsync(key);
    return result;
  }

  async simple(key) {
    // 修正错误抛出的标准写法
    throw new Error('Simple Error: ' + key);
  }
}

2. 修正Mocha测试代码

describe('Redis Manager Test:', function() {
  // 用async/await简化测试逻辑,无需手动调用done
  it('catches simple errors', async function () {
    let manager = new RedisManager('BOGUSHOST');
    let key = 'testKey';
    try {
      await manager.simple(key);
      // 如果走到这里说明未抛出预期错误,测试失败
      assert.fail('Expected error was not thrown');
    } catch (err) {
      // 正确断言错误消息
      assert.strictEqual(err.message, 'Simple Error: ' + key);
    }
  });

  it('catches Redis errors', async function () {
    let manager = new RedisManager('BOGUSHOST');
    try {
      await manager.getKeys('Some');
      assert.fail('Expected Redis connection error was not thrown');
    } catch (err) {
      console.log('Caught Error: ' + err);
      // 断言错误类型或特征
      assert.ok(err instanceof Error);
      assert.ok(err.message.includes('ECONNREFUSED') || err.message.includes('connect'));
    }
  });
});

修改点说明

  1. 监听Redis error事件
    这是解决问题的核心,未监听的error事件会直接终止Node进程,Promise的catch完全没机会执行。

  2. 封装连接状态Promise
    Redis创建连接是异步操作,hgetallAsync在连接未就绪时不会主动抛出连接错误。通过connectionPromise,我们能确保在执行Redis命令前,先等待连接状态确定,连接失败时会直接reject,进入后续的catch块。

  3. 修正测试代码的错误

    • 第一个测试里的err = Error('...')是赋值操作,不是比较,改成断言错误消息才是正确的判断方式;
    • 用Mocha支持的async/await写法替代done回调,代码更简洁易读。

额外建议

redis@2.8.0是比较老旧的版本了,如果你可以升级,建议换成最新的@redis/client,它原生支持Promise和async/await,不需要依赖bluebird做promisify,对错误的处理也更符合现代JS的预期。

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

火山引擎 最新活动