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

React Native异常疑问:FetchTool自定义错误未被AccessService捕获

问题:React Native中catch块无法捕获自定义错误,反而得到“幽灵错误”

环境配置

  • React Native 运行在 Android 虚拟设备(API 23)
  • Android Gradle 插件版本:7.2.0
  • Gradle 版本:7.4.2
  • build.gradle 关键配置片段:
buildscript {
    ext {
        kotlinVersion = "1.5.0"
        RNNKotlinVersion = kotlinVersion
        buildToolsVersion = "31.0.0"
        minSdkVersion = 23
        compileSdkVersion = 31
        targetSdkVersion = 28
...
}

问题场景

向本地服务器地址 http://10.0.2.2:8888/myapp/auth 发起登录验证POST请求,服务器返回500响应码(表示邮箱/密码错误)。

FetchTool 中针对500场景抛出了自定义错误 new Error(JSON.stringify(responseObject)),但调用它的 AccessService 的catch块捕获到的却是如下“幽灵错误”:

LOG ERROR..:{"line":25722,"column":33,"sourceURL":"http://10.0.2.2:8081/index.bundle?platform=android&dev=true&minify=false&app=com.myapp&modulesOnly=false&runModule=true"}

相关代码

AccessService 代码

export default class AccessService {

    static confirmLogon(email, password) {

        const login = {
            email: email,
            password: password
        };

        const headers = {
            'Content-Type': 'application/json',
        }

        const uri = properties.appurl + '/auth';

        return FetchTool.post(uri, headers, login)
            .then(credentials => {
                return credentials;
            })
            .catch(error => {
                // 此处捕获到幽灵错误
                console.log('ERROR..:' + JSON.stringify(error))
                throw error;
            });
    }

}

FetchTool 代码

export default class FetchTool {

    static post(uri, headers, playload) {
        return this.executeRequest(uri, headers, playload, 'POST');
    }

    static executeRequest(uri, headers, playload, method) {

        if (headers['Content-Type'] && headers['Content-Type'] == 'application/json') {
            playload = JSON.stringify(playload);
        }

        const requestInfo = {
            method: method,
            body: playload,
            headers: headers
        }

        // uri = http://10.0.2.2:8888/myapp/auth
        
        return fetch(uri, requestInfo)
            .then(response => {

                if (response.ok) {
                    // 正常返回JSON的逻辑
                } else {
                    return response.text()
                        .then(responseText => JSON.parse(responseText))
                        .then(responseObject => {
                            if(responseObject.statusCode === 500) {
                                // 错误触发点
                                throw new Error(JSON.stringify(responseObject)); 
                            } else {
                                // 其他错误处理逻辑
                            }
                        })
                }
            })

    }
    
}

原因分析

  1. Promise链错误传递断层:FetchTool 中 response.text().then().then() 的子Promise链里抛出的错误,没有被正确向上传递到外层Promise。React Native调试环境遇到未被显式捕获的Promise错误时,会输出包含bundle位置的“幽灵错误”,而非自定义错误内容。
  2. Error对象序列化丢失信息:直接用 JSON.stringify(error) 序列化原生Error对象时,只会输出其内部的位置属性(line、column等),自定义的message等关键信息会被忽略,导致日志里看不到错误详情。

解决方案

方案1:修复Promise链的错误传递

在FetchTool的错误处理子链末尾添加catch,确保错误能向上传递到AccessService的catch块:

return fetch(uri, requestInfo)
    .then(response => {
        if (response.ok) {
            // 正常返回JSON的逻辑
        } else {
            return response.text()
                .then(responseText => {
                    try {
                        return JSON.parse(responseText);
                    } catch (parseError) {
                        throw new Error('响应解析失败');
                    }
                })
                .then(responseObject => {
                    if(responseObject.statusCode === 500) {
                        throw new Error(JSON.stringify(responseObject)); 
                    } else {
                        // 其他错误处理逻辑
                    }
                })
                // 显式捕获子链错误并向上抛出
                .catch(fetchError => {
                    throw fetchError;
                });
        }
    })

方案2:正确读取Error对象的信息

在AccessService的catch块中,不要直接序列化Error对象,而是读取其message属性:

.catch(error => {
    console.log('ERROR..:' + error.message);
    throw error;
});

方案3:使用自定义错误类(可选)

创建自定义错误类携带业务信息,避免原生Error的序列化限制:

// 定义自定义错误类
class ApiError extends Error {
    constructor(message, statusCode) {
        super(message);
        this.statusCode = statusCode;
        this.name = 'ApiError';
    }
}

// 在FetchTool中抛出自定义错误
throw new ApiError('邮箱/密码错误', 500);

// 在AccessService中捕获并打印
.catch(error => {
    console.log(`ERROR..: ${error.name} - ${error.message} (状态码: ${error.statusCode})`);
    throw error;
});

内容的提问来源于stack exchange,提问作者araraujo

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.25 22:09:19