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
相关产品推荐
相关产品推荐

