【前端】在 TypeScript 中使用 AbortController 取消异步请求
在 TypeScript 中使用 AbortController
来取消异步请求,尤其是像 fetch
这样的请求,可以提供一种优雅的方式来中止长时间运行的操作。下面是一个详细的步骤说明,展示如何在 TypeScript 中使用 AbortController
取消 fetch
请求。
步骤 1:创建 AbortController
实例
首先,你需要创建一个 AbortController
实例,并从中获取 signal
对象。这个 signal
对象将在请求中使用,以便在需要时取消请求。
const controller = new AbortController();
const signal = controller.signal;
步骤 2:发送带有 signal
的请求
当使用 fetch
发送请求时,传递 signal
作为选项的一部分。如果请求被取消,fetch
将会抛出一个带有 name
属性为 "AbortError"
的错误。
fetch('/api/data', { signal }).then(response => {if (!response.ok) {throw new Error(`HTTP error! Status: ${response.status}`);}return response.json();}).then(data => {console.log(data);}).catch(error => {if (error.name === 'AbortError') {console.log('Fetch operation was aborted');} else {console.error('Fetch operation failed:', error);}});
步骤 3:取消请求
在某个时刻,如果你决定要取消请求,只需调用 controller.abort()
方法即可。
setTimeout(() => {controller.abort();
}, 3000); // 取消请求将在3秒后发生
示例:完整的代码示例
下面是一个完整的示例,展示了如何使用 AbortController
来取消一个 fetch
请求:
// 示例:使用 AbortController 取消 fetch 请求
const controller = new AbortController();
const signal = controller.signal;// 在3秒后取消请求
setTimeout(() => {controller.abort();
}, 3000);// 发送带有 signal 的请求
fetch('/api/data', { signal }).then(response => {if (!response.ok) {throw new Error(`HTTP error! Status: ${response.status}`);}return response.json();}).then(data => {console.log('Received data:', data);}).catch(error => {if (error.name === 'AbortError') {console.log('Fetch operation was aborted');} else {console.error('Fetch operation failed:', error);}});
在使用 AbortController
取消请求后,如果需要重新尝试发送请求,可以采用几种不同的策略。下面将介绍如何在 TypeScript 中实现重新尝试的逻辑。
1. 手动重新尝试
最简单的方法是在请求被取消后手动重新触发请求。这种方法适用于你知道何时应该重新尝试的情况,比如在某些用户操作之后。
const controller = new AbortController();
const signal = controller.signal;// 发送带有 signal 的请求
fetch('/api/data', { signal }).then(response => {if (!response.ok) {throw new Error(`HTTP error! Status: ${response.status}`);}return response.json();}).then(data => {console.log('Received data:', data);}).catch(error => {if (error.name === 'AbortError') {console.log('Fetch operation was aborted');// 手动重新尝试setTimeout(() => {controller = new AbortController();signal = controller.signal;fetch('/api/data', { signal }).then(response => {if (!response.ok) {throw new Error(`HTTP error! Status: ${response.status}`);}return response.json();}).then(data => {console.log('Retried and received data:', data);}).catch(error => {console.error('Retry failed:', error);});}, 5000); // 5秒后重新尝试} else {console.error('Fetch operation failed:', error);}});
2. 使用重试库
另一种方法是使用重试库,例如 p-retry
或者 retry
,它们可以自动处理重试逻辑,并提供灵活的配置选项。
示例:使用 p-retry
首先安装 p-retry
:
npm install p-retry
然后在代码中使用它:
import retry from 'p-retry';async function fetchData(signal: AbortSignal): Promise<any> {return fetch('/api/data', { signal }).then(res => res.json());
}retry(() => fetchData(signal),{ retries: 3, factor: 2, minTimeout: 1000, maxTimeout: 3000 }
).then(data => {console.log('Received data:', data);}).catch(error => {console.error('Fetch operation failed:', error);});
3. 自定义重试逻辑
你也可以自己编写重试逻辑,根据需要调整重试次数、间隔时间等参数。
const MAX_RETRIES = 3;
const RETRY_INTERVAL = 1000; // 间隔时间(毫秒)async function fetchData(signal: AbortSignal): Promise<any> {return fetch('/api/data', { signal }).then(res => res.json());
}async function retryFetch(signal: AbortSignal, retriesLeft: number = MAX_RETRIES): Promise<any> {try {return await fetchData(signal);} catch (error) {if (error.name === 'AbortError' && retriesLeft > 0) {console.log(`Fetch operation was aborted, retrying... (${retriesLeft} attempts left)`);await new Promise(resolve => setTimeout(resolve, RETRY_INTERVAL));return retryFetch(signal, retriesLeft - 1);} else {throw error;}}
}retryFetch(signal).then(data => {console.log('Received data:', data);}).catch(error => {console.error('Fetch operation failed:', error);});
4. 使用 AbortController
的组合策略
你还可以结合使用 AbortController
和重试逻辑,以实现更复杂的场景。例如,在某些条件下取消请求,并在满足一定条件时重新尝试。
const controller = new AbortController();
const signal = controller.signal;// 在满足某些条件下取消请求
setTimeout(() => {if (/* 某些条件 */) {controller.abort();}
}, 3000);// 发送带有 signal 的请求
fetch('/api/data', { signal }).then(response => {if (!response.ok) {throw new Error(`HTTP error! Status: ${response.status}`);}return response.json();}).then(data => {console.log('Received data:', data);}).catch(error => {if (error.name === 'AbortError') {console.log('Fetch operation was aborted');// 根据条件重新尝试if (/* 满足重新尝试的条件 */) {const newController = new AbortController();const newSignal = newController.signal;fetch('/api/data', { signal: newSignal }).then(response => {if (!response.ok) {throw new Error(`HTTP error! Status: ${response.status}`);}return response.json();}).then(data => {console.log('Retried and received data:', data);}).catch(error => {console.error('Retry failed:', error);});}} else {console.error('Fetch operation failed:', error);}});
注意事项
- 兼容性:请确保你的浏览器或 Node.js 版本支持
AbortController
。对于不支持的环境,你可能需要考虑使用 polyfills。 - 信号传播:如果你在请求中使用了中间件(例如代理服务器),请注意
AbortController
的信号可能不会被正确传播。在这种情况下,你可能需要手动处理信号的传播。 - 资源释放:取消请求后,确保适当释放资源。例如,如果请求涉及文件上传,取消请求后应确保删除临时文件。
使用 AbortController 的好处
- 更好的用户体验:用户可以在长时间的请求未完成前取消请求,减少无响应的情况。
- 资源管理:可以避免因长时间未完成的请求而导致的资源浪费。
- 灵活性:为异步操作提供了一种灵活的控制机制。
通过这种方式,你可以有效地在 TypeScript 中使用 AbortController
来管理异步请求的生命周期。