Node.js中fs.watch()内使用await导致Redis连接阻塞的问题排查
好问题!在fs.watch的回调里使用async/await本身语法上是完全允许的,但结合你的场景,确实有几个常见的坑可能导致你遇到的停滞问题,咱们一步步分析:
1. 未捕获的静默Promise拒绝是最可能的原因
fs.watch的回调是异步执行的,而Node.js对于未处理的Promise拒绝,在部分版本中不会主动抛出到全局控制台,只会静默失败——这就会让你感觉代码卡在await处没动静。
看你的Redis连接代码:
async connectionToRedisClient() { return await this.connect('connect') .then(() => { log.info('Connected'); }) .catch(() => log.error('Error connecting to redis')); }
这里的catch只捕获了connect的错误,但没有抛出错误,也没有返回可用状态。如果connect之后还有其他异步操作(比如main函数里依赖Redis连接的逻辑)出现错误,或者this.connect本身有隐藏异常,都会变成未处理的Promise拒绝,导致代码停滞。
修复建议:
- 给
fs.watch的async回调加上完整的try/catch,捕获所有可能的错误:fs.watch('./targets', async (event, filename) => { try { console.log('event is: ' + event); if (filename === 'file.txt') { await main(); } else { console.log('Incorrect file passed'); } } catch (err) { // 打印所有隐藏错误 console.error('Watch callback failed:', err); } }); - 在全局添加未处理Promise拒绝的监听,防止遗漏:
process.on('unhandledRejection', (err) => { console.error('Unhandled Promise Rejection:', err); }); - 修改Redis连接函数,抛出错误让上层处理,而非静默吃掉:
async connectionToRedisClient() { try { await this.connect('connect'); log.info('Connected'); } catch (err) { log.error('Error connecting to redis', err); throw err; // 抛出错误,让await main()能捕获到 } }
2. Redis连接的重复调用导致状态冲突
单独调用await connectionToRedisClient()时,是一次性建立连接;但在fs.watch回调里,每次文件变化都会触发main,进而重复调用connectionToRedisClient。如果你的Redis客户端没有判断当前连接状态,反复调用connect可能会让客户端处于pending状态,导致代码卡住。
修复建议:
在连接函数里先检查客户端状态,避免重复连接:
async connectionToRedisClient() { // 根据你的Redis客户端类型调整状态判断,比如ioredis用isReady,node-redis用isOpen if (this.client?.isReady) { log.info('Already connected to Redis'); return; } try { await this.connect('connect'); log.info('Connected'); } catch (err) { log.error('Error connecting to redis', err); throw err; } }
3. fs.watch的重复事件触发干扰
某些操作系统(比如Windows)下,fs.watch会对同一个文件变化触发多次事件(比如保存文件时会触发change+rename),短时间内重复调用main会导致多个Redis连接请求同时发起,可能造成客户端阻塞。
修复建议:
加个简单的防抖锁,避免短时间内重复执行:
let isProcessing = false; fs.watch('./targets', async (event, filename) => { if (isProcessing) return; try { isProcessing = true; console.log('event is: ' + event); if (filename === 'file.txt') { await main(); } else { console.log('Incorrect file passed'); } } catch (err) { console.error('Watch callback failed:', err); } finally { isProcessing = false; } });
总结
async/await在fs.watch回调里本身没有问题,你的情况大概率是未捕获的静默错误或者Redis连接重复调用导致的状态异常。先按照上面的步骤添加错误捕获,再检查Redis连接的状态管理,应该就能解决停滞问题。
内容的提问来源于stack exchange,提问作者james

