我开发了 React 应用程序并使用一些全局可用的对象实例来处理组件逻辑中的重复任务。实现很简单:
class SomeService {
// Some service logic
}
const someService = new SomeService();
export default someService;
然后我使用
import
导入 React 功能组件中的实例,并在这些对象上调用正确的方法。这样我实现了负责从远程获取数据的APIService
:
class APIService {
async get<Entity extends IEntity>(
url: string,
data: IQueryParams<Entity>,
onSuccess?: (response: IResponse) => void,
onError?: (error: IResponseDetails) => void,
onNotFound?: (error: IResponseDetails) => void
): Promise<boolean> {
// Some logic
}
// Other methods...
}
因此,每当我想在组件中执行 GET 请求时,我都可以使用:
APIService.get(url, data, (response: IResponse) => {
// Success
), (error: IResponseDetails) => {
// Error
});
最近,我决定在
AbortController
类中实现 APIService
功能,以便在组件卸载时中止 fetch
请求。我想通过以下方式实现它:
useEffect(() => {
const request = APIService.get(url, data, (response: IResponse) => {
// Success
), (error: IResponseDetails) => {
// Error
});
return () => {
APIService.abort(request);
}
}, []);
在底层,
APIService
类包含private queue: Promise<boolean>[]
属性,用于跟踪在fetch
调用期间附加到它的所有未决承诺,并在承诺解决时将其删除。
APIService.abort
方法应该以Promise<boolean>
为参数,在queue: Promise<boolean>[]
数组中找到promise并触发AbortSignal
。
我遇到的问题是,如果传递的承诺包含在
APIService.abort
数组中,我无法检查 queue
方法(我使用 get
将其添加到 this.queue.push(promise)
方法中)。我知道 Promise
是一个对象,并通过引用传递,但无论我尝试使用 queue
(始终返回 this.queue.indexOf(promise)
)在 -1
数组中找到它还是通过 ===
比较,它都不会'检测已添加的承诺(就像引用在某个时刻发生了更改)。
有趣的是,如果我将
abort
属性公开并在类外调用它,则 queue
方法中的查找可以正常工作:
APIService.queue.push(request);
APIService.abort(request);
但是如果
push
是在APIService.get
方法内部完成的,则不起作用。我缺少什么?我有什么办法可以理解这种行为吗?
我尝试将
queue
类型扩展为以下内容:
private queue = {
id: number,
promise: Promise<boolean>
}[]
并在将新 Promise 添加到
Math.random()
时将 id
作为 queue
传递(以便查看该对象是否未在其他地方实例化),但在 abort
方法中进行查找显示值是一致的(尽管传递的参数仍然不会出现在 this.queue[i].promise
的 i < this.queue.length
中)。
我还尝试了
setTimeout
,以查看 Promise
参考在状态更改期间是否不会以某种方式发生变化,但我没有观察到任何类似的行为。
现场示例:StackBlitz 演示
如果推送是在
方法内部完成的,则不起作用。我错过了什么?APIService.get
原因是你的
get
方法被声明为 async
,这意味着它创建并返回自己的 Promise
,它包装了函数体的执行。因此,当您在函数内部创建一个 Promise,将其推送到队列,然后 return
它时,所做的就是用显式返回的 Promise 解析外部(隐式)Promise。虽然它们会得到相同的结果,但它们是不同的对象。因此,您可以删除 async
来完成这项工作。
我想按以下方式实现它,其中
方法应该以APIService.abort
作为参数,在Promise<boolean>
数组中找到承诺并触发queue
。AbortSignal
无论如何,这并不是中止信号的设计用途。相反,接受中止信号作为
get
方法的参数!在效果中创建一个中止控制器,并传递其信号,然后从效果清理函数中触发它:
useEffect(() => {
const controller = new AbortController();
const request = APIService.get(url, data, (response: IResponse) => {
// Success
), (error: IResponseDetails) => {
// Error
}, controller.signal);
return () => {
controller.abort();
}
}, []);