通过封装一个统一的请求管理器对象来实现外观模式,该对象内部维护一个 `abortcontroller` 实例,并提供 `request`、`cancel` 和 `cancelall` 方法。调用方只需调用外观对象的接口即可发起带有取消能力的请求,无需直接操作底层的 `fetch` api 或控制器实例。

如何设计请求管理器的核心接口
核心接口应包含初始化控制器、绑定取消信号以及暴露取消动作的方法。在 JavaScript 中,`fetch` 等网络请求支持通过 `signal` 选项接收 `AbortSignal`,一旦信号被触发,请求将立即中止并抛出异常。外观对象负责创建这个控制器,并将其信号传递给具体的请求函数,同时保留对控制器的引用以便后续取消。

class RequestFacade {
constructor() {
this.controller = new AbortController();
this.signal = this.controller.signal;
}
async request(url, options = {}) {
const fetchOptions = {
...options,
signal: this.signal
};
try {
const response = await fetch(url, fetchOptions);
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
return await response.json();
} catch (error) {
if (error.name === 'AbortError') {
console.log('Request cancelled');
} else {
throw error;
}
}
}
cancel() {
this.controller.abort();
}
}
如何处理并发请求与独立取消
当需要处理多个独立请求时,外观模式可以扩展为管理一组控制器。每个请求拥有独立的 `AbortController`,允许单独取消某个特定请求而不影响其他正在进行的请求。这种设计保持了接口的简洁性,同时提供了细粒度的控制能力,适用于列表加载、搜索建议等场景。

class AdvancedRequestFacade {
constructor() {
this.requests = new Map();
this.idCounter = 0;
}
request(id, url, options = {}) {
if (this.requests.has(id)) {
this.requests.get(id).abort();
}
const controller = new AbortController();
this.requests.set(id, controller);
const fetchOptions = { ...options, signal: controller.signal };
fetch(url, fetchOptions)
.then(res => res.json())
.then(data => {
this.requests.delete(id);
return data;
})
.catch(err => {
if (err.name !== 'AbortError') {
this.requests.delete(id);
}
throw err;
});
}
cancel(id) {
const controller = this.requests.get(id);
if (controller) {
controller.abort();
this.requests.delete(id);
}
}
cancelAll() {
this.requests.forEach(controller => controller.abort());
this.requests.clear();
}
}
基于取消操作后状态清理的具体问题
在取消请求后,必须确保相关的控制器引用被清除,以防止内存泄漏。特别是在 `AdvancedRequestFacade` 中,如果请求成功完成或发生非中止错误,需要从 `requests` Map 中移除对应的控制器。若仅依赖 `cancel` 方法清理,而忽略请求自然结束时的清理逻辑,会导致 Map 中积累无用的控制器对象,进而占用内存。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











