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

NodeJS中实现带延迟的Promise重试连接方法求助

我来帮你搞定这个带延迟重试的连接逻辑!你遇到的问题应该是没把事件驱动的Promise和重试逻辑正确结合起来,不管是手动实现还是用第三方库,都得注意几个关键细节——比如每次重试要重置socket、移除旧的事件监听,还有正确触发重试的时机。

手动实现重试逻辑(无第三方库)

这种方式不需要依赖任何外部库,逻辑清晰易懂,适合需要完全控制重试流程的场景。我们把核心连接逻辑抽成内部方法,再写一个带递归重试的包装方法:

const net = require('net');
const Modbus = require('jsmodbus');

// 可配置的重试参数
const MAX_RETRIES = 5;    // 最大重试次数
const RETRY_DELAY = 3000; // 每次重试间隔(毫秒)

class YourModbusClient {
  constructor(unitID, options) {
    this.unitID = unitID;
    this.options = options;
    this._initSocket(); // 初始化socket和client
  }

  // 初始化/重置socket实例
  _initSocket() {
    this.socket = new net.Socket();
    this.client = new Modbus.client.TCP(this.socket, this.unitID);
  }

  // 核心连接逻辑(内部方法)
  _connect() {
    return new Promise((resolve, reject) => {
      // 先移除所有旧的事件监听,避免重复触发
      this.socket.removeAllListeners('connect');
      this.socket.removeAllListeners('error');

      this.socket.connect(this.options);
      
      this.socket.on('connect', () => {
        logger.info('*****CONNECTION MADE*****');
        resolve();
      });
      
      this.socket.on('error', (error) => {
        logger.error(`连接失败: ${error.message}`);
        this.disconnect();
        reject(error);
      });
    });
  }

  // 带重试的对外连接方法
  async connectWithRetry(retriesLeft = MAX_RETRIES) {
    try {
      await this._connect();
      return; // 连接成功,直接返回
    } catch (error) {
      if (retriesLeft <= 0) {
        logger.error('已达到最大重试次数,放弃连接');
        throw new Error('多次连接失败,请检查设备状态');
      }
      
      logger.info(`将在${RETRY_DELAY/1000}秒后重试... (剩余重试次数: ${retriesLeft})`);
      // 延迟指定时间后递归重试
      await new Promise(resolve => setTimeout(resolve, RETRY_DELAY));
      return this.connectWithRetry(retriesLeft - 1);
    }
  }

  // 断开连接并重置socket
  disconnect() {
    if (this.socket) {
      this.socket.destroy();
      this._initSocket(); // 重置socket,为下次连接做准备
    }
  }
}

关键细节说明:

  • 每次重试前重置socket实例:出错后的socket无法复用,必须重新创建
  • 移除旧事件监听:避免多次绑定导致同一事件被触发多次
  • 用async/await递归实现重试:逻辑直观,容易控制终止条件

使用promise-retry库的正确姿势

如果你还是想用第三方库,那大概率是之前的用法没适配事件驱动的Promise。下面是正确的使用方式,帮你避开踩坑点:

首先安装库:

npm install promise-retry

然后修改你的类:

const net = require('net');
const Modbus = require('jsmodbus');
const promiseRetry = require('promise-retry');

class YourModbusClient {
  constructor(unitID, options) {
    this.unitID = unitID;
    this.options = options;
    this._initSocket();
  }

  _initSocket() {
    this.socket = new net.Socket();
    this.client = new Modbus.client.TCP(this.socket, this.unitID);
  }

  _connect() {
    return new Promise((resolve, reject) => {
      this.socket.removeAllListeners('connect');
      this.socket.removeAllListeners('error');

      this.socket.connect(this.options);
      
      this.socket.on('connect', () => {
        logger.info('*****CONNECTION MADE*****');
        resolve();
      });
      
      this.socket.on('error', (error) => {
        logger.error(`连接失败: ${error.message}`);
        this.disconnect();
        reject(error);
      });
    });
  }

  // 用promise-retry包装连接逻辑
  connectWithRetry() {
    return promiseRetry((retry, attemptNumber) => {
      logger.info(`正在尝试第${attemptNumber}次连接`);
      
      return this._connect().catch((error) => {
        // 只对特定的连接错误进行重试(比如连接被拒绝、超时)
        const retryableErrors = ['ECONNREFUSED', 'ETIMEDOUT', 'EHOSTUNREACH'];
        if (retryableErrors.includes(error.code)) {
          logger.info(`第${attemptNumber}次连接失败,准备重试`);
          return retry(error); // 调用retry()触发下一次重试
        }
        // 非连接类错误直接抛出,不再重试
        throw error;
      });
    }, {
      retries: 5,        // 最大重试次数(包含第一次尝试)
      minTimeout: 3000,  // 每次重试的最小延迟(毫秒)
      maxTimeout: 3000,  // 每次重试的最大延迟(毫秒),这里设置固定间隔
      factor: 1,         // 延迟倍数,1表示固定延迟
      randomize: false   // 不随机化延迟时间
    });
  }

  disconnect() {
    if (this.socket) {
      this.socket.destroy();
      this._initSocket();
    }
  }
}

关键细节说明:

  • 必须在回调里调用retry(error)才会触发重试,不能直接返回错误
  • 增加错误类型判断:避免对所有错误都重试,只处理连接相关的异常
  • 同样要重置socket和移除事件监听,这是保证重试有效的核心前提

不管你选哪种方式,调用的时候都直接用connectWithRetry()即可,比如:

const client = new YourModbusClient(1, { host: '192.168.1.100', port: 502 });
client.connectWithRetry()
  .then(() => {
    // 连接成功后的操作
    logger.info('连接初始化完成');
  })
  .catch((error) => {
    logger.error('最终连接失败:', error);
  });

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.09 15:02:42