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

优化Promise.race递归调用实现绘图工具事件流逻辑

解决递归Promise.race导致大量Promise未resolve的问题

你的核心问题在于:每次调用counterFlow时,都会同时创建doCounter和quit两个Promise,当其中一个事件触发(比如点击)并resolve对应的Promise后,另一个事件的监听器(比如keydown)并没有被移除,导致它对应的Promise一直处于pending状态,递归次数越多,积累的pending Promise就越多。

优化方案核心思路

不管哪个事件先触发,都要主动移除另一个未触发的事件监听器,同时结束对应的Promise(避免pending),这样既能保持全局状态整洁,也不会积累未resolve的Promise。

修改后的代码实现

function startCount(node) {
  const counter = Object.create(counterMethods);
  counter.node = node;
  counter.number = 0;
  // 存储当前活跃的事件处理器,方便后续移除
  counter.activeHandlers = {};
  return counter;
}

const counterMethods = {
  // 封装通用的事件监听Promise创建方法
  createEventPromise(eventType, filterFn) {
    return new Promise((resolve) => {
      const handler = (e) => {
        // 满足过滤条件才触发
        if (filterFn?.(e) !== false) {
          this.node.removeEventListener(eventType, handler);
          delete this.activeHandlers[eventType];
          resolve(e);
        }
      };
      this.node.addEventListener(eventType, handler);
      this.activeHandlers[eventType] = handler;
    });
  },

  // 主动移除所有活跃的事件监听器
  clearActiveListeners() {
    Object.entries(this.activeHandlers).forEach(([eventType, handler]) => {
      this.node.removeEventListener(eventType, handler);
    });
    this.activeHandlers = {};
  },

  async counterFlow() {
    try {
      // 创建点击和退出的事件Promise
      const clickPromise = this.createEventPromise('click');
      const quitPromise = this.createEventPromise('keydown', (e) => e.key === 'Escape');

      const result = await Promise.race([
        clickPromise.then(() => 'click'),
        quitPromise.then(() => 'exit')
      ]);

      if (result === 'exit') {
        console.log(`已退出,累计点击次数:${this.number}`);
        // 退出时清除所有剩余监听器
        this.clearActiveListeners();
      } else if (result === 'click') {
        this.number += 1;
        console.log(`当前点击次数:${this.number}`);
        // 点击后清除退出监听器(避免残留)
        this.clearActiveListeners();
        // 递归进入下一轮监听
        await this.counterFlow();
      }
    } catch (err) {
      console.error('监听流程出错:', err);
      this.clearActiveListeners();
    }
  },
};

关键改动说明

  1. 通用事件Promise封装:createEventPromise统一处理事件监听、过滤和移除逻辑,同时把活跃的处理器存在实例的activeHandlers中,方便后续批量清理。
  2. 强制清理监听器:不管是点击还是退出触发,都会调用clearActiveListeners移除所有剩余的事件监听器,确保没有残留的监听器和pending Promise。
  3. 递归时使用await:递归调用counterFlow时加上await,保证异步流程的顺序性,避免出现多个并行的监听流程。

这样修改后,每次监听周期结束都会清理所有未触发的监听器,不会积累pending Promise,同时也不需要维护额外的全局状态变量,符合你保持状态整洁的需求。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.17 20:22:03