如何在Axios自定义Hook中捕获请求取消错误?
问题
我正在使用Axios构建一个用于获取数据的自定义Hook。当请求在收到响应前被取消时,会产生一个无法在try/catch块中捕获的取消错误。我可以在请求后用.catch捕获它,但这样会有两处错误处理位置,还混合了ES6的then/catch与ES7的try/catch语法。请问该如何解决?
说明
- 采用了Axios文档中推荐的AbortController方法
- setTimeout函数仅用于调试
当前代码
const useFetch = ( dataUrl ) => { // Set initial state variables const [ data, setData ] = useState( [] ); const [ catchError, setCatchError ] = useState( null ); const [ isLoading, setIsLoading ] = useState( false ); const controllerRef = useRef( false ); // Run the effect on mount useEffect( () => { let isMounted = true; // Define request cancellation signal const controller = new AbortController(); const { signal } = controller; // Fetch data function declaration const fetchData = async ( url ) => { setIsLoading( true ); try { // Fetch the response setTimeout( async () => { const response = await axios.get( url, { signal } ) // Catch cancellation error (couldn't be fetched in the catch block) .catch( e => e.code === "ERR_CANCELED" && console.log( "Fetch Request Cancelled" ) ); // Set the data if ( isMounted ) { setData( response.data ); setCatchError( null ); } }, 2000 ) } catch ( err ) { // Catch the error if ( isMounted ) { setCatchError( err.message ); setData( [] ); } } finally { // Set loading to initial state isMounted && setTimeout( () => setIsLoading( false ), 2000 ); } } // Call the Fetch Data function fetchData( dataUrl ) // Cleanup the request on cancellation return function cleanUp() { console.log( 'Clean up function' ); isMounted = false; controller.abort(); }; }, [ dataUrl ] ); return { data, catchError, isLoading }; } export default useFetch;
诉求
如何将下图中的取消错误捕获到try/catch块中:
解决方案
问题根源
你的代码里setTimeout包裹了await axios.get,这个setTimeout的回调是独立的异步函数,它抛出的错误无法被外层的try/catch捕获——外层try只能捕获当前函数栈的同步错误和直接await的异步错误,setTimeout的回调属于另一个执行上下文。同时,混合使用.catch和try/catch导致错误处理逻辑分散。
修正步骤
- 移除嵌套的异步回调:将
axios.get直接放在fetchData的try块内,调试用的延迟改为await Promise形式,纳入当前异步流程。 - 统一用try/catch处理错误:所有错误(包括取消错误)都会进入
catch块,通过判断错误码区分取消请求和其他异常。 - 简化状态更新逻辑:确保仅在组件挂载时执行状态更新,避免内存泄漏。
修正后的代码
import { useState, useEffect } from 'react'; import axios from 'axios'; const useFetch = (dataUrl) => { const [data, setData] = useState([]); const [catchError, setCatchError] = useState(null); const [isLoading, setIsLoading] = useState(false); useEffect(() => { let isMounted = true; const controller = new AbortController(); const { signal } = controller; const fetchData = async (url) => { setIsLoading(true); try { // 调试用延迟,纳入当前异步流程 await new Promise(resolve => setTimeout(resolve, 2000)); const response = await axios.get(url, { signal }); if (isMounted) { setData(response.data); setCatchError(null); } } catch (err) { if (isMounted) { // 区分取消请求与其他错误 if (err.code === "ERR_CANCELED") { console.log("Fetch Request Cancelled"); setCatchError("请求已取消"); } else { setCatchError(err.message); setData([]); } } } finally { if (isMounted) { setTimeout(() => setIsLoading(false), 2000); } } }; fetchData(dataUrl); return function cleanUp() { console.log('Clean up function'); isMounted = false; controller.abort(); }; }, [dataUrl]); return { data, catchError, isLoading }; }; export default useFetch;
关键说明
- 调试用的
setTimeout改为await new Promise(...)后,成为fetchData异步流程的一部分,错误能被外层try/catch捕获。 - 取消错误会进入
catch块,通过err.code === "ERR_CANCELED"判断并处理,统一了错误处理位置,不再混合两种语法。 - 移除了无用的
controllerRef,controller在useEffect内部声明,cleanup函数可直接访问。
内容的提问来源于stack exchange,提问作者Elwyn
相关产品推荐
相关产品推荐

