如何区分Fetch请求中的网络错误与主动Promise链中断
Fetch请求中区分HTTP状态错误与网络错误的实现方案
问题场景
现有JavaScript代码设计预期为:当Web端点返回2xx类(即200区间的成功状态码)响应时执行对应成功逻辑,返回404状态码时执行专属处理逻辑,返回非404的其他HTTP状态码时执行对应错误逻辑,同时需要正确处理网络异常场景。
原有实现代码如下:
fetch('https://httpstat.us/404') .then((r) => { if (r.ok) { console.log(`response 200-ish`) return r // 进入下一个.then() } else { console.log(`response is not 200-ish`) return Promise.reject(r.status) // 在此处中断成功Promise链 } }) .then((r) => { console.log('do stuff for 200-ish') }) .catch((err) => { if (err === 404) { console.log('do 404 stuff') } else { console.log('do stuff because of another response from the API') } // 此处无法区分网络错误与其他HTTP状态错误 })
现有代码问题
不同场景下的运行表现如下:
- 请求
https://httpstat.us/200时,代码运行符合预期 - 请求
https://httpstat.us/404时,代码运行符合预期 - 请求
https://httpstat.us/500(既非200也非404,但属于API服务器正常返回的响应)时,代码仅碰巧运行符合预期 - 请求
https://httpstat.usXXXXXXXX/500(非法URL,会触发网络错误)时,代码运行不符合预期
问题根源:catch块中最后的else分支会捕获所有未被显式校验的错误,无法区分「主动中断Promise链抛出的HTTP状态码错误」和「真实发生的网络层错误」。
实现方案
核心思路:不要直接抛出裸的状态码数值,而是用带专属标识的错误对象包装HTTP响应错误,和原生网络错误做明确区分。
原生Fetch触发的网络错误本身是TypeError类型实例,只要给主动抛出的HTTP错误加上可识别的专属标记,就能在catch块中精准判断错误来源。
修正后代码
// 自定义HTTP错误类,专门包装服务器返回的非2xx响应错误 class HttpError extends Error { constructor(status, response) { super(`HTTP request failed with status ${status}`) this.name = 'HttpError' this.status = status // 存储HTTP状态码 this.response = response // 存储原始响应对象,可按需读取响应体 } } fetch('https://httpstat.us/404') .then((r) => { if (r.ok) { console.log(`response 200-ish`) return r } // 非2xx响应统一抛出自定义HttpError,和原生网络错误做区隔 throw new HttpError(r.status, r) }) .then((r) => { console.log('do stuff for 200-ish') }) .catch((err) => { // 优先判断是否为主动抛出的HTTP响应错误 if (err instanceof HttpError) { if (err.status === 404) { console.log('do 404 stuff') } else { console.log(`handle other HTTP error, status code: ${err.status}`) } } else { // 剩余错误为原生网络错误,包含断网、非法URL、CORS跨域拦截等场景 console.log('handle network error', err) } })
方案说明
- 自定义
HttpError类继承原生Error,专门承载服务器正常返回的非2xx响应错误,除状态码外还可按需附加响应体、请求参数等上下文信息 - 所有非2xx响应统一抛出
HttpError实例,不再直接抛出裸数字 - catch块中先通过
instanceof判断错误类型,属于HttpError则按状态码分支处理业务逻辑,否则统一按网络异常处理 - 如果不想额外定义类,也可以用更轻量的实现:抛出错误时给普通对象加专属标记,比如
throw { type: 'HTTP_ERROR', status: r.status },catch块中先判断err.type === 'HTTP_ERROR'再处理状态码即可。自定义类的方式语义更清晰,也能保留完整错误栈方便调试。
内容的提问来源于stack exchange,提问作者WoJ
相关产品推荐
相关产品推荐

