不用 try...catch 也能安全处理异步错误,关键是将 Promise 拒绝态“数据化”为统一的 { data, error } 结构,通过 safeCall、to、拦截器或 useAsync 等方式实现标准化错误处理与类型友好扩展。

不用 try...catch 也能安全处理异步错误,关键在于把错误“数据化”——让 Promise 拒绝态也返回标准的 { data, error } 结构,和成功态保持接口一致。
用 .then().catch() 链式转形
在请求发起后立即统一包裹,把 reject 转成 resolve 并填入 error 字段:
function safeCall(promise) {
return promise
.then(data => ({ data, error: null }))
.catch(error => ({ data: null, error }));
}
// 使用
safeCall(fetch('/api/user'))
.then(result => {
if (result.error) {
console.error('加载失败:', result.error.message);
return;
}
console.log('用户数据:', result.data);
});
封装成可 await 的工具函数(推荐)
配合 async/await 更自然,避免嵌套 .then:
async function to<T>(promise: Promise<T>): Promise<[T | null, Error | null]> {
try {
const data = await promise;
return [data, null];
} catch (err) {
return [null, err as Error];
}
}
// 使用(注意解构顺序:data 在前,error 在后)
const [user, err] = await to(fetch('/api/user').then(r => r.json()));
if (err) {
// 处理错误
} else {
// 使用 user
}
这个模式在 TypeScript 中类型友好,还能轻松扩展为 { data, error, loading } 等状态字段。
分析 Java Spring Boot 后端 Controller 代码,自动生成 Markdown 格式的接口文档。当用户说“生成 Java 接口文档”、“生成 API 文档”、“从 Spring 代码生成文档”时触发。
立即学习“Java免费学习笔记(深入)”;
拦截器层面统一(Axios / 自定义 fetch)
在请求库层面拦截响应与错误,直接返回结构化结果:
// Axios 示例
axios.interceptors.response.use(
response => ({ data: response.data, error: null }),
error => ({ data: null, error: error.response?.data || error.message })
);
// 后续所有请求都自动返回 { data, error }
const { data, error } = await axios.get('/api/profile');
if (error) handleApiError(error);
组合 useAsync 类 Hook(React 场景)
把状态、加载、错误、数据全部收敛到一个对象里,彻底告别组件内 try/catch:
function useAsync(fn, deps = []) {
const [state, setState] = useState({ data: null, error: null, loading: false });
useEffect(() => {
let mounted = true;
setState(prev => ({ ...prev, loading: true }));
fn()
.then(data => mounted && setState({ data, error: null, loading: false }))
.catch(error => mounted && setState({ data: null, error, loading: false }));
return () => { mounted = false };
}, deps);
return state;
}
// 组件中
const { data, error, loading } = useAsync(() => api.getUser(id));
if (loading) return <Spinner />;
if (error) return <ErrorView error={error} />;
return <Profile user={data} />;

















