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

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.15 07:49:46