“可中止”获取的想法在 2017 年 AbortController
发布时诞生。这为我们提供了一种方法来放弃由 fetch()
启动的 API 请求——即使是多个调用——无论何时我们想要。
这是一个使用 AbortController
取消 fetch()
请求的超级简单示例
const controller = new AbortController();
const res = fetch('/', { signal: controller.signal });
controller.abort();
console.log(res); // => Promise(rejected): "DOMException: The user aborted a request"
当用于现代的 setTimeout
接口时,您真的可以看出它的价值。这样,在例如 10 秒后使获取超时变得非常简单
function timeout(duration, signal) {
return new Promise((resolve, reject) => {
const handle = setTimeout(resolve, duration);
signal?.addEventListener('abort', e => {
clearTimeout(handle);
reject(new Error('aborted'));
});
});
}
// Usage
const controller = new AbortController();
const promise = timeout(10000, controller.signal);
controller.abort();
console.log(promise); // => Promise(rejected): "Error: aborted"
但最大的新闻是 addEventListener
现在接受 中止信号,从 Chrome 88 开始。这有什么好处呢?它可以用作 removeEventListener
的替代方案
const controller = new AbortController();
eventTarget.addEventListener('event-type', handler, { signal: controller.signal });
controller.abort();
比这更酷的是什么?嗯,因为 AbortController
能够一次中止多个可取消的请求,它简化了删除多个监听器的过程,一步到位。我已经发现它在拖放方面特别有用。
以下是我在没有 AbortController
的情况下如何编写拖放脚本,依赖两个 removeEventListener
实例来清除两个不同的事件
// With removeEventListener
el.addEventListener('mousedown', e => {
if (e.buttons !== 1) return;
const onMousemove = e => {
if (e.buttons !== 1) return;
/* work */
}
const onMouseup = e => {
if (e.buttons & 1) return;
window.removeEventListener('mousemove', onMousemove);
window.removeEventListener('mouseup', onMouseup);
}
window.addEventListener('mousemove', onMousemove);
window.addEventListener('mouseup', onMouseup); // Can’t use `once: true` here because we want to remove the event only when primary button is up
});
通过最新的更新,addEventListener
接受 signal
属性作为其第二个参数,允许我们调用 abort()
一次来停止所有事件监听器,当它们不再需要时
// With AbortController
el.addEventListener('mousedown', e => {
if (e.buttons !== 1) return;
const controller = new AbortController();
window.addEventListener('mousemove', e => {
if (e.buttons !== 1) return;
/* work */
}, { signal: controller.signal });
window.addEventListener('mouseup', e => {
if (e.buttons & 1) return;
controller.abort();
}, { signal: controller.signal });
});
再次,Chrome 88 目前是唯一一个 addEventListener
正式接受 AbortSignal 的地方。虽然其他主要浏览器,包括 Firefox 和 Safari,支持 AbortController
,但将它的信号与 addEventListener
集成目前还不可行……并且没有迹象表明他们计划在这方面进行努力。也就是说,有一个 polyfill 可用。
嘿,这真的很酷!感谢您的帖子!
在最后一个 JS 示例中,
abortController.signal
不应该是controller.signal
吗?因为没有名为abortController
的变量已修复。谢谢。
是的,它不起作用。
如果您查看 https://github.com/whatwg/dom/pull/919 中列出的平台错误票证,实际上看起来 Firefox 和 Safari 都已经实现了它。
在调用
controller.abort()
后,事件处理程序函数会被垃圾回收吗?如果不是,似乎使用 AbortController 会影响性能。
该功能似乎在 Chrome 88 中处于一个标志的后面,并且只有在 Chrome 90 中默认启用。根据其他浏览器的信号,这个问题表明该功能计划在 Firefox 86 版本中发布。
……并且 相应的 Webkit 问题 已经解决,所以我相信我们很快就会有很好的支持!
chromestatus.com 不是跟踪其他供应商状态的理想场所,因为他们不会自动跟踪它们,并且他们不会经常更新这部分内容……
事件监听器中的信号现在在 Firefox 上受支持,但 iOS 和桌面 Safari 仍然缺乏支持。
https://mdn.org.cn/en-US/docs/Web/API/EventTarget/addEventListener#browser_compatibility
我们今天检查了它是否现在在所有浏览器中都起作用,我可以确认 Safari 15 和 Firefox 101 也支持它。
如果您必须支持 Safari 14,可以使用这个微小的 Polyfill。
https://github.com/nuxodin/lazyfill/blob/main/monkeyPatches/addEventListenerSignal.js