clearInterval()失效求助:为何无法正常清除定时器?
为什么clearInterval无法正常清除定时器?
让我来拆解你代码里的问题,这几个细节导致了定时器没被正确清除:
1. Promise执行器里的错误处理完全错了
在iCounter === repeats的分支里,你用了throw err,但Promise的executor(就是new Promise((resolve, reject) => {...})里的回调)内部抛出的错误,不会被外部的.catch()捕获,而且你在throw之后写的console.log(err)和return Promise.reject(err)根本不会执行——throw已经直接中断了代码流。正确的做法是直接调用reject(err)来拒绝Promise。
2. 异步回调的竞态问题
每次client.screenshot()都是异步操作,当你调用clearInterval(interval)时,可能还有之前发起的截图请求处于pending状态。这些请求完成后还是会进入then回调,但此时定时器虽然被清了,后续逻辑还是会跑,看起来就像定时器没被清除一样。
3. 没有跟踪Promise的完成状态
你的函数没标记Promise是否已经resolve或reject,导致即使已经找到目标并resolve了,之前pending的截图回调还是会继续执行,干扰状态判断。
修复后的代码
function fnIsOnScreen(img, repeats = 5, desc, wait = 2000) { let iCounter = 0; let intervalId = null; // 标记Promise是否已完成,避免异步回调重复执行 let isTaskCompleted = false; return new Promise((resolve, reject) => { console.log("Click is running"); intervalId = setInterval(() => { // 如果任务已经完成,直接跳过本次定时器回调 if (isTaskCompleted) return; client.screenshot() .then((data) => { // 再次检查,防止异步回调在任务完成后才执行 if (isTaskCompleted) return; const buf = Buffer.from(data.value, 'base64'); // 推荐用Buffer.from替代new Buffer const img1 = cv.imdecode(buf); const result = img1.matchTemplate(img, 5).minMaxLoc(); if (result.maxVal >= 0.65) { isTaskCompleted = true; clearInterval(intervalId); console.log(`Object found #${iCounter} ${desc}`); resolve(result); } else { console.log(`Cant see object yet #${iCounter}`); iCounter++; if (iCounter === repeats) { isTaskCompleted = true; clearInterval(intervalId); const err = new Error(`Object not found : ${desc}`); console.log(err); reject(err); } } }) .catch((err) => { // 捕获截图请求的错误,及时终止任务 isTaskCompleted = true; clearInterval(intervalId); console.log(err); reject(err); }); }, wait); }) .catch((err) => { // 双重保障:即使外部捕获到错误,也确保定时器被清除 if (intervalId) clearInterval(intervalId); console.log(err); throw err; // 或者返回Promise.reject(err),根据你的业务需求选择 }); }
关键修复说明
- 添加
isTaskCompleted状态标记:在定时器回调和异步回调的开头都检查这个标记,确保任务完成后不会再执行任何逻辑,彻底避免竞态问题。 - 用
reject替代throw:在Promise执行器内部,直接调用reject(err)来传递错误,这样错误才能被外部的.catch()正确捕获。 - 错误分支也清除定时器:不管是截图失败还是达到重试次数,都及时清除定时器并标记任务完成。
- 替换
new Buffer为Buffer.from:new Buffer已经被废弃,官方推荐用Buffer.from更安全。 - 模板字符串优化日志:让日志代码更简洁易读。
现在你连续调用这个函数时,每个调用都会创建独立的定时器和状态标记,不会互相干扰,定时器也能在任务完成(找到目标或报错)时立即被清除。
内容的提问来源于stack exchange,提问作者trixo
相关产品推荐
相关产品推荐

