使用Fetch API请求不存在资源时为何直接进入catch块?
问题:Thunderbird中Fetch API处理404错误异常的疑问
我首次在新版Thunderbird中使用Fetch API,运行如下代码:
let uri = whatever(); window.fetch(uri) .then((response) => { console.log(`got response with status ${response.status}`); if (!response.ok) { throw new Error(`thrown response status: ${response.status}`); } return response.text(); }) .then((xhtml) => doStuff(xhtml)) .catch((err) => console.error(`fetching failed: ${err}`));
当请求存在的资源时,代码正常运行(doStuff()执行相应操作);但请求有效服务器上不存在的页面时,控制台输出:
fetching failed: TypeError: NetworkError when attempting to fetch resource.
此时会进入catch块,但并非抛出我在then块中定义的错误。请问这是什么原因?以及如何区分资源不存在与其他错误(如无法连接服务器、超时等)?
分析与解决方案
原因分析
标准Fetch API的逻辑是:只有真正的网络故障(比如连不上服务器、DNS解析失败、超时)才会直接reject promise;像404、500这类HTTP错误状态码,promise依然会resolve,让你在then块里自行判断处理。但新版Thunderbird对Fetch做了定制化修改,把4xx/5xx这类HTTP错误也归类为网络错误,直接触发了promise reject,导致你写的错误判断代码根本没机会执行。
区分不同错误的解决方法
要绕过这个环境限制,你可以改用async/await结合try/catch,同时自定义错误标识来区分不同场景:
let uri = whatever(); async function fetchResource() { try { const response = await window.fetch(uri); // 先确认响应对象有效 if (!response) { throw new Error("网络连接异常"); } console.log(`got response with status ${response.status}`); if (!response.ok) { // 给HTTP错误添加状态码属性,方便后续区分 const httpError = new Error(`HTTP错误: ${response.status}`); httpError.statusCode = response.status; throw httpError; } const xhtml = await response.text(); doStuff(xhtml); } catch (err) { if (err.statusCode) { // 处理HTTP状态码相关错误 if (err.statusCode === 404) { console.error(`资源不存在: ${err.message}`); } else if (err.statusCode >= 500) { console.error(`服务器内部错误: ${err.message}`); } else { console.error(`HTTP请求错误: ${err.message}`); } } else if (err.message.includes("NetworkError")) { // 处理真正的网络故障(无法连接、超时等) console.error(`网络连接失败: ${err.message}`); } else { // 兜底处理其他未知错误 console.error(`请求失败: ${err.message}`); } } } fetchResource();
另外,你也可以查阅Thunderbird的扩展开发文档,看看是否有配置项能让Fetch回归标准行为——也就是把HTTP错误状态码视为resolve而非reject,这样就能沿用你最初的then链式写法。
内容的提问来源于stack exchange,提问作者einpoklum
相关产品推荐
相关产品推荐

