探索 JavaScript 中的 AbortController API:不仅仅是中断 HTTP 请求

在 JavaScript 中处理异步操作时,通常需要灵活的控制机制来中止任务。AbortController API 提供了一个强大且通用的方式来终止异步操作,不仅可以中断 HTTP 请求,还可以应用于多种异步任务,如事件监听、流操作等。在本文中,我们将详细探讨 AbortController 的使用场景、AbortSignal 的静态方法、事件处理中的中止机制,以及在实际开发中的一些最佳实践。

AbortController 简介

AbortController 是 JavaScript 中的全局类,专门用于管理和中止异步操作。通过创建一个 AbortController 实例,我们可以获取两个核心功能:

  1. signal 属性:用于监控是否有中止信号发出,能够传递给异步操作,比如 fetch 请求或事件监听器。
  2. .abort() 方法:可以随时手动触发中止信号,进而取消异步操作。

基本用法如下:

javascript 复制代码
const controller = new AbortController();
const signal = controller.signal;

fetch('/api/data', { signal })
  .then(response => response.json())
  .catch(error => {
    if (error.name === 'AbortError') {
      console.log('Fetch request was aborted');
    } else {
      console.error('Fetch error:', error);
    }
  });

// 取消请求
controller.abort();

事件监听器的取消

在事件处理中,我们可以通过将 AbortController.signal 传递给 addEventListener 的第三个参数,使得在中止时自动移除事件监听器:

javascript 复制代码
const controller = new AbortController();
const button = document.querySelector('#myButton');

button.addEventListener('click', () => {
  console.log('Button clicked');
}, { signal: controller.signal });

// 在某个条件下中止事件监听
controller.abort();

这样,当调用 controller.abort() 时,按钮的点击事件监听器会被自动移除,避免冗余的事件绑定和内存泄漏。

Fetch 请求的中断

fetch 请求原生支持 AbortSignal,可以在请求发出后随时中断:

javascript 复制代码
const controller = new AbortController();

fetch('/api/long-running-task', { signal: controller.signal })
  .then(response => response.json())
  .catch(err => {
    if (err.name === 'AbortError') {
      console.log('Fetch request aborted');
    } else {
      console.error('Fetch failed:', err);
    }
  });

// 稍后取消请求
setTimeout(() => {
  controller.abort();
}, 5000);  // 5秒后取消请求

在网络请求延时过长或用户切换页面等场景下,中断 fetch 请求可以显著提升用户体验。

Axios 请求的中断

从 v0.22.0 开始,Axios 支持以 fetch API 方式------ AbortController 取消请求:

javascript 复制代码
const controller = new AbortController();

axios.get('/foo/bar', {
   signal: controller.signal
}).then(function(response) {
   //...
});
// 取消请求
controller.abort()
  • 当然还可以使用 CancelToken 取消一个请求,但需要注意的是此 API 从 v0.22.0 开始已被弃用,不应在新项目中使用。
js 复制代码
const CancelToken = axios.CancelToken;
const source = CancelToken.source();

axios.get('/user/12345', {
  cancelToken: source.token
}).catch(function (thrown) {
  if (axios.isCancel(thrown)) {
    console.log('Request canceled', thrown.message);
  } else {
    // 处理错误
  }
});

axios.post('/user/12345', {
  name: 'new name'
}, {
  cancelToken: source.token
})

// 取消请求(message 参数是可选的)
source.cancel('Operation canceled by the user.');

Node.js 请求的中断

在 Node.js 环境中,http 模块的请求同样支持使用 AbortSignal 来中止请求。以下是一个简单的示例:

javascript 复制代码
const http = require('http');
const { AbortController } = require('abort-controller');

const controller = new AbortController();

const req = http.get('http://example.com', { signal: controller.signal }, res => {
  res.on('data', chunk => {
    console.log(`Data: ${chunk}`);
  });
});

setTimeout(() => {
  controller.abort();
  console.log('Request aborted');
}, 5000);  // 5秒后中止请求

这在处理长时间的 HTTP 请求时非常有用,尤其是需要通过中断请求来节省资源。

AbortSignal 的静态方法

AbortSignal 还提供了两个静态方法,timeoutany,可以增强中止操作的灵活性:

  1. AbortSignal.timeout(duration):可以设置一个自动超时的中止信号。
javascript 复制代码
const signal = AbortSignal.timeout(5000);  // 5秒后自动中止

fetch('/api/data', { signal })
  .catch(err => {
    if (err.name === 'AbortError') {
      console.log('Fetch request timed out');
    }
  });
  1. AbortSignal.any([signal1, signal2, ...]):允许组合多个中止信号,任何一个信号触发中止,都会终止任务。
javascript 复制代码
const controller1 = new AbortController();
const controller2 = new AbortController();

const signal = AbortSignal.any([controller1.signal, controller2.signal]);

fetch('/api/data', { signal })
  .catch(err => {
    if (err.name === 'AbortError') {
      console.log('Fetch request aborted by one of the signals');
    }
  });

// 任意一个 controller 调用 abort 都会中止 fetch 请求
controller1.abort();

取消流操作

在现代 JavaScript 中,AbortController 也可以用于中止可写流(WritableStream)的操作。在处理流式数据时,这种中止机制非常重要,可以有效管理数据传输和资源消耗:

javascript 复制代码
const controller = new AbortController();
const stream = new WritableStream({
  write(chunk, controller) {
    // 写入逻辑
  },
  abort(reason) {
    console.log('Stream aborted:', reason);
  }
}, { signal: controller.signal });

// 中止流操作
controller.abort('Manual abort');

中止任何逻辑

AbortController 不仅限于终止网络请求,还可以在任意可中止的逻辑中使用。比如,我们可以在 ORM 事务处理中使用它:

javascript 复制代码
async function performTransaction() {
  const controller = new AbortController();

  try {
    await startTransaction({ signal: controller.signal });
    // 事务逻辑
  } catch (error) {
    if (error.name === 'AbortError') {
      console.log('Transaction aborted');
    }
  }

  controller.abort();
}

中止错误处理

当中止事件发生时,AbortSignal 会附带一个 AbortError,开发者可以根据不同的中止原因进行错误处理。例如,我们可以传递一个自定义的中止原因并在错误处理中使用:

javascript 复制代码
const controller = new AbortController();
controller.abort('Operation was manually cancelled');

fetch('/api/data', { signal: controller.signal })
  .catch(err => {
    if (err.name === 'AbortError') {
      console.log('Fetch aborted with reason:', err.message);  // 输出自定义原因
    }
  });

兼容性

AbortController 具有良好的兼容性,几乎所有现代浏览器都支持它。此外,Node.js 也通过实验性功能实现了对 AbortController 的支持。它已经成为开发者工具箱中不可或缺的部分。

总结

AbortController 是一个非常有用且灵活的 API,适用于多种异步操作的中止和管理。无论是取消 HTTP 请求、事件监听,还是流式数据处理,AbortController 都能为开发者提供精细的控制能力。通过合理使用它,我们可以更好地优化代码的健壮性和用户体验。

相关推荐
Southern Wind20 分钟前
H5页面在线预览pdf
javascript·pdf
May_Xu_20 分钟前
vue3+less使用主题定制(多主题定制)可切换主题
前端·javascript·vue.js·vue·less·css3
Elastic 中国社区官方博客26 分钟前
将你的 Kibana Dev Console 请求导出到 Python 和 JavaScript 代码
大数据·开发语言·前端·javascript·python·elasticsearch·ecmascript
闲人陈二狗37 分钟前
vue3中的pinia的使用方法
开发语言·javascript·ecmascript
小华同学ai1 小时前
jsMind:炸裂项目,用JavaScript构建的思维导图库,GitHub上的热门开源项目
javascript·开源·github
夏河始溢1 小时前
一七一、React性能优化方式
javascript·react.js·性能优化
叶不休1 小时前
DOM---鼠标事件类型(移入移出)
开发语言·前端·javascript·css·chrome·前端框架·html
言6662 小时前
vue点击菜单,出现2个相同tab,啥原因
前端·javascript·vue.js
Z_ One Dream2 小时前
css 在 hover 子元素时,不要让父元素触发 hover 效果
前端·javascript·css
Python私教9 小时前
Flutter颜色和主题
开发语言·javascript·flutter