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

如何避免AbortError: signal is aborted without reason错误

如何避免AbortError: signal is aborted without reason错误

这个错误的核心原因是:当你尝试中止一个已经完成的请求对应的AbortController时,浏览器会抛出AbortError——因为此时信号关联的请求流程已经全部结束,abort()操作已经没有意义了。你代码里的问题在于,请求完成后没有及时清理控制器,导致cancel()方法会遍历到这些已完成请求的控制器,调用abort()时触发错误。

下面给你几个可行的解决方案,按推荐程度排序:


方案1:请求完成后自动清理控制器(最推荐)

在send方法中,当请求完全完成(包括await response.text()执行完之后),把对应的控制器从this.controllers中移除。这样后续调用cancel()时,只会处理那些真正还在进行中的请求控制器。

修改后的send方法:

async send(url) {
   const controller = new AbortController();
   const signal = controller.signal;
   // 假设requestId是当前请求的唯一标识,你原代码中未定义需自行补充
   if (options == null) { options = {} }
   if (options.signal == null) { options.signal = signal };

   this.controllers.set(requestId, controller);
   
   try {
     var response = await fetch(url, options);
     var text = await response.text();
     return response;
   } finally {
     // 不管请求成功还是失败,只要流程结束就移除控制器
     this.controllers.delete(requestId);
   }
}

用finally块可以确保无论请求成功还是抛出错误,都会清理控制器,既避免后续误操作,也能防止内存泄漏。


方案2:跟踪请求完成状态,仅中止未完成请求

如果因为某些原因你需要保留已完成请求的控制器,可以新增一个Map来记录已完成的请求ID,在cancel()时同时检查请求是否完成。

修改后的MyClass代码片段:

class MyClass {
  constructor() {
    this.controllers = new Map();
    this.completedRequests = new Map(); // 新增:记录已完成的请求ID
  }

  async send(url) {
    // ... 原有初始化代码 ...
    try {
      var response = await fetch(url, options);
      var text = await response.text();
      this.completedRequests.set(requestId, true); // 标记请求已完成
      return response;
    } catch (e) {
      // 请求失败也标记为完成(可根据业务需求调整)
      this.completedRequests.set(requestId, true);
      throw e;
    }
  }

  cancel() {
    if (this.controllers) {
      this.controllers.forEach((value, key, map) => {
          // 只中止「未被中止且未完成」的请求
          if (!value.signal.aborted && !this.completedRequests.has(key)) {
              value.abort();
          }
      });
      // 可选:清理已完成的请求记录
      this.completedRequests.clear();
    }
  }

  // ... 其他方法保持不变 ...
}

方案3:捕获并忽略预期内的AbortError

如果上面的方案都不适合,你可以直接在调用abort()时捕获这个错误——因为它属于预期内的情况(请求已经完成,中止操作无意义),可以安全忽略。

修改后的cancel方法:

cancel() {
   if (this.controllers) {
      this.controllers.forEach((value, key, map) => {
          if (value.signal.aborted == false) {
              try {
                  value.abort();
              } catch (e) {
                  // 只忽略请求完成后触发的AbortError
                  if (!(e instanceof DOMException && e.name === 'AbortError')) {
                      throw e; // 其他类型的错误正常抛出
                  }
              }
          }
      });
   }
}

这个方案是兜底的防御式处理,即使有遗漏的已完成请求控制器,也不会导致代码报错中断。


补充说明

你在MDN看到的注释When abort() is called, the fetch() promise rejects with an AbortError,描述的是请求还在进行中时调用abort()的正常行为——此时fetch的Promise会被中止并抛出错误。而你遇到的错误是请求已经完成时调用abort()的场景,属于额外的错误情况,MDN的注释没有覆盖到这一点。

备注:内容来源于stack exchange,提问作者1.21 gigawatts

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.04.14 08:48:02