出乎意料,这个 JS API 远比你想的更强大。。。
大家好,我是虎哥。
今天咱们来聊聊一个可能被你低估的 JavaScript API,AbortController。提到它,很多人第一反应可能就是中止 fetch 请求,没错,这确实是它的典型用法。
不过,你可能不知道的是,这个 API 的能耐远远超出了请求中断的范畴,它的强大简直超乎你的想象,甚至可以说用好了能省下不少代码量和脑细胞。今天我就来给大家掰扯掰扯,看看它到底有多强。
首先来说说,AbortController 是 JavaScript 的一个全局类,用来管理异步操作的中止。你创建一个 AbortController 实例后,会得到一个 signal 和 abort() 方法。
signal 是一个 AbortSignal 对象,我们可以把它传递给支持中止的 API,而 abort() 方法可以让操作中止。虽然这个东西看起来没啥新鲜的,但用好了你会发现它在实际开发中能派上大用场。
常见的例子当然就是用来中止网络请求,特别是用 fetch 请求数据的时候。当请求不再需要,比如用户离开页面或者切换组件时,我们就可以中止请求,避免占用不必要的资源。
比如,我们用下面的代码来搞个简单的 fetch 请求,然后在某个时刻主动中止它。
const controller = new AbortController();fetch('https://api.example.com/data', { signal: controller.signal })
.then(response => response.json())
.then(data => console.log('数据获取成功:', data))
.catch(error => {
if (error.name === 'AbortError') {
console.log('请求被取消');
} else {
console.error('请求出错:', error);
}
});
// 在需要中止请求的地方调用 abort() 方法
controller.abort();
上面这段代码,就是利用 AbortController 来控制 fetch 请求。当我们调用 abort() 方法的时候,这个请求就会被中止,并且抛出一个 AbortError。
这个用法是大家比较熟悉的了,但我想说的是,AbortController 其实能干的事儿可多了去了,不只是用来中止请求而已。
比如说事件监听器。平时我们用 addEventListener() 添加监听器,再用 removeEventListener() 去手动删除,有时候写得满屏都是 add 和 remove,眼睛看得都花了。
其实,AbortController 就能帮我们优雅地解决这个问题。它允许我们为事件监听器提供一个中止信号,这样当调用 abort() 时,所有关联的监听器都会自动移除,简直不要太省心。举个例子:
const controller = new AbortController();window.addEventListener('resize', () => console.log('窗口尺寸变化'), { signal: controller.signal });
window.addEventListener('scroll', () => console.log('页面滚动'), { signal: controller.signal });
// 中止所有监听器
controller.abort();
就这么简单,我们把同一个 AbortController 的 signal 传递给不同的事件监听器,调用一次 abort() 就能干掉所有关联的监听器。这在复杂应用中能帮我们省去不少繁琐的资源清理工作,也让代码看起来更简洁。
除此之外,AbortSignal 还可以用在流处理、WebSocket 等场景。举个例子,你用 WebSocket 接收数据的时候,如果需要在某个条件下停止监听,也可以使用 AbortSignal.any() 来组合多个中止信号,只要有一个信号触发,中止操作就会执行。
这样我们可以实现用户点击按钮停止接收,或者当服务器主动断开连接时自动中止的逻辑。
const publicController = new AbortController();
const internalController = new AbortController();
const socket = new WebSocket('wss://example.com/socket');socket.addEventListener('message', event => console.log('收到消息:', event.data), {
signal: AbortSignal.any([publicController.signal, internalController.signal]),
});
document.getElementById('stopButton').addEventListener('click', () => {
publicController.abort(); // 手动中止
console.log('停止接收消息');
});
setTimeout(() => {
internalController.abort(); // 模拟自动中止
console.log('自动中止接收');
}, 5000);
通过 AbortSignal.any() 把多个信号组合在一起,我们可以灵活地控制中止操作,这样无论是用户手动停止,还是系统自动停止,都能用一种优雅的方式来处理。
在流处理场景中,AbortController 也能大显身手。比如,在写入 WritableStream 时,我们可以利用 AbortSignal 来中止写操作。这样做的好处是,一旦触发中止,就能立即停止数据写入,并执行清理工作,避免系统资源浪费。
const controller = new AbortController();const stream = new WritableStream({
write(chunk) {
console.log('写入数据:', chunk);
},
abort() {
console.warn('写入操作被中止');
}
});
const writer = stream.getWriter();
writer.write('数据块 1');
writer.write('数据块 2');
controller.signal.addEventListener('abort', () => writer.abort());
controller.abort();
你看,在这个例子中,我们通过监听 AbortSignal 的 abort 事件,轻松地实现了对流写入操作的中止。这样一来,代码的灵活性和可维护性大大提升。
此外,AbortController 还有个隐藏技能,就是可以为中止事件提供自定义的原因。
比如,当我们调用 abort() 时传递一个消息,然后通过 signal.reason 获取这个原因,就能根据不同的中止原因采取不同的处理策略。这在复杂的应用中非常有用,能够更好地进行错误处理和日志记录。
const controller = new AbortController();controller.signal.addEventListener('abort', () => {
console.log('中止原因:', controller.signal.reason);
});
fetch('https://api.example.com/data', { signal: controller.signal })
.catch(error => {
if (error.name === 'AbortError') {
console.error('请求被取消:', controller.signal.reason);
} else {
console.error('请求出错:', error);
}
});
controller.abort('用户主动取消');
这段代码里,我们在调用 abort() 时传递了一个字符串“用户主动取消”,并通过监听中止事件输出了这个中止原因。如此一来,我们的程序就能更精准地追踪每个请求的取消情况。
再说个让你惊讶的地方吧,AbortController 不仅能在浏览器里用,在 Node.js 里它同样好使。举个栗子,我们可以用 AbortController 来中止 HTTP 请求。在 Node.js 的 http 模块里,我们也可以传递 signal 来控制请求的中止行为。
const http = require('http');
const { AbortController } = require('abort-controller');const controller = new AbortController();
const req = http.request('http://example.com', { signal: controller.signal }, res => {
res.on('data', chunk => console.log(`响应数据: ${chunk}`));
});
req.on('error', err => {
if (err.name === 'AbortError') {
console.log('请求被取消');
} else {
console.error('请求出错:', err);
}
});
req.end();
setTimeout(() => {
controller.abort();
}, 2000);
在这个例子中,我们模拟了一个 HTTP 请求,并在 2 秒后中止了它。这展示了 AbortController 的适用范围不仅限于浏览器端,甚至在服务端的异步操作中也同样适用。
总的来说,AbortController 是一个不折不扣的多面手,表面上看只是用来中止请求的小工具,实际上还能大大简化复杂应用中的资源管理和事件处理逻辑。
通过合理利用它的中止信号,我们可以更加优雅地管理事件监听器、流处理、WebSocket 连接,甚至是数据库事务的回滚操作。
只要你敢用,这个 API 就能帮你省去很多重复的繁琐操作,还能让代码更加简洁清晰。说实话,能写代码写到这个地步,我觉得这 API 都快成艺术品了。
至于兼容性,AbortController很久之前就已经被纳入了 Web 兼容性 Baseline,从 2019 年 3 月起,大部分主流浏览器都可以使用了,所以不用担心兼容性问题。
那么大家怎么看呢?欢迎大家评论区留言分享!
对编程、职场感兴趣的同学,大家可以联系我微信:golang404,拉你进入“程序员交流群”。
资料包含了《IDEA视频教程》、《最全python面试题库》、《最全项目实战源码及视频》及《毕业设计系统源码》,总量高达650GB。全部免费领取!全面满足各个阶段程序员的学习需求。