You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Redux Toolkit中使用Promise.all遇请求异常仍执行问题排查

问题分析

  1. 请求并行触发无法中断:handleUpdate中同时dispatch两个thunk,会立刻发起两个API请求,即使其中一个快速返回错误,另一个请求已经发出,无法撤回。
  2. Thunk错误处理未正确抛出异常:updateUser和updateCurrentUserAvatar的catch块仅打印错误但未重新抛出,导致thunk的Promise始终处于resolved状态,Promise.all无法捕获错误,后续错误处理逻辑失效。
  3. 冗余手动触发fulfilled action:createAsyncThunk在请求成功后会自动触发对应的fulfilled case,无需手动dispatchupdateCurrentUserAvatar.fulfilled和updateUser.fulfilled。

解决方案

1. 修复Thunk的错误抛出逻辑

修改两个createAsyncThunk的catch块,重新抛出错误,确保API失败时thunk的Promise进入rejected状态:

export const updateUser = createAsyncThunk('superadmin/updateProfile', async (payload) => {
    try {
        const response = await updateProfile({
            email: payload.email,
            fullname: payload.fullname,
            dob: payload.dob,
            gender: payload.gender,
            phoneNumber: payload.phoneNumber,
        });
        if (response.statusCode === 200) {
            return response;
        } else {
            throw new Error('Response status is not 200');
        }
    } catch (error) {
        console.log(error);
        // 重新抛出错误,让thunk的Promise变为rejected
        throw error;
    }
});

export const updateCurrentUserAvatar = createAsyncThunk('superadmin/updateAvatar', async (data) => {
    try {
        const formData = new FormData();
        formData.append('file', data);
        const response = await axios.post('User/avatar', formData);
        if (response.statusCode === 200) {
            return response;
        } else {
            throw new Error('Response status is not 200');
        }
    } catch (error) {
        console.log(error);
        // 重新抛出错误
        throw error;
    }
});

2. 控制请求触发时机(二选一)

选项A:顺序执行请求(避免无效请求)

如果希望只有第一个请求成功时才发起第二个,减少无效API调用:

const handleUpdate = async () => {
    try {
        // 先更新用户资料,成功后再上传头像
        const userUpdateResult = await dispatch(updateUser(formData)).unwrap();
        const avatarUpdateResult = await dispatch(updateCurrentUserAvatar(updateImage)).unwrap();
        
        // 两个请求都成功,执行后续逻辑(如关闭弹窗)
        setOpen(false);
    } catch (error) {
        console.error('更新失败:', error);
        // 这里可以添加用户提示,比如alert('更新失败,请检查输入')
    }
};

注:使用.unwrap()可以获取thunk的实际返回值或直接抛出错误,因为dispatch返回的是带有unwrap方法的Promise。

选项B:并行请求+取消令牌(支持并行可中断)

如果需要并行请求,但希望在其中一个失败时取消另一个未完成的请求,可使用axios的取消令牌:
首先修改thunk,添加取消令牌支持:

export const updateUser = createAsyncThunk('superadmin/updateProfile', async (payload, { signal }) => {
    try {
        const source = axios.CancelToken.source();
        // 监听abort信号,触发时取消请求
        signal.addEventListener('abort', () => {
            source.cancel('因其他请求失败,终止当前请求');
        });
        
        const response = await updateProfile({
            email: payload.email,
            fullname: payload.fullname,
            dob: payload.dob,
            gender: payload.gender,
            phoneNumber: payload.phoneNumber,
            cancelToken: source.token
        });
        if (response.statusCode === 200) {
            return response;
        } else {
            throw new Error('Response status is not 200');
        }
    } catch (error) {
        // 忽略取消请求的错误
        if (!axios.isCancel(error)) {
            console.log(error);
            throw error;
        }
    }
});

export const updateCurrentUserAvatar = createAsyncThunk('superadmin/updateAvatar', async (data, { signal }) => {
    try {
        const source = axios.CancelToken.source();
        signal.addEventListener('abort', () => {
            source.cancel('因其他请求失败,终止当前请求');
        });
        
        const formData = new FormData();
        formData.append('file', data);
        const response = await axios.post('User/avatar', formData, {
            cancelToken: source.token
        });
        if (response.statusCode === 200) {
            return response;
        } else {
            throw new Error('Response status is not 200');
        }
    } catch (error) {
        if (!axios.isCancel(error)) {
            console.log(error);
            throw error;
        }
    }
});

然后在组件中使用Promise.all并处理取消逻辑:

const handleUpdate = async () => {
    const promise1 = dispatch(updateCurrentUserAvatar(updateImage));
    const promise2 = dispatch(updateUser(formData));
    
    try {
        const [avatarResult, userResult] = await Promise.all([
            promise1.unwrap(),
            promise2.unwrap()
        ]);
        
        // 两个请求都成功
        setOpen(false);
    } catch (error) {
        console.error('更新失败:', error);
        // 取消另一个未完成的请求
        promise1.abort();
        promise2.abort();
    }
};

3. 移除冗余代码

删除handleUpdate中手动触发fulfilled action的代码:

// 移除以下两行
dispatch(updateCurrentUserAvatar.fulfilled(result1.payload));
dispatch(updateUser.fulfilled(result2.payload));

内容的提问来源于stack exchange,提问作者Quang Phú Nguyễn Hoàng

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.29 18:47:26