使用 Fetch API 时实现错误处理

Umar Hansa
Umar Hansa

本文介绍了使用 Fetch API 时的一些错误处理方法。通过 Fetch API,您可以向远程网络资源发出请求。当您进行远程网络调用时,您的网页可能会受到各种潜在网络错误的影响。

下面几部分将说明潜在的错误,以及如何编写代码,提供应对错误和意外网络状况的合理功能级别。弹性代码可确保用户满意,并为您的网站维持标准的服务水平。

预测潜在的网络错误

本部分介绍了这样一种情景:用户创建了名为 "My Travels.mp4" 的新视频,然后尝试将视频上传到视频共享网站。

使用抓取时,可以轻松考虑用户成功上传视频的顺利路径。不过,还有一些其他途径不那么顺畅,但 Web 开发者必须规划这些途径。此类(不愉快的)路径可能是由于用户错误、意外的环境条件或视频共享网站上的错误造成的。

用户错误示例

  • 用户上传图片文件(如 JPEG),而不是视频文件。
  • 用户开始上传错误的视频文件。然后,在上传过程中,用户会指定要上传的正确视频文件。
  • 用户在上传视频时不小心点击了“取消上传”。

环境变化示例

  • 上传视频时,互联网连接会断开。
  • 浏览器会在视频上传过程中重新启动。
  • 上传视频时,视频共享网站的服务器会重新启动。

视频分享网站错误示例

  • 视频分享网站无法处理包含空格的文件名。它需要的是诸如 "My_Travels.mp4""MyTravels.mp4" 之类的名称,而不是 "My Travels.mp4"
  • 视频共享网站上传的视频不得超出可接受的文件大小上限。
  • 视频分享网站不支持所上传的视频中的视频编解码器。

这些例子在现实世界中确实会发生。您以前可能遇到过此类示例!我们从上述每个类别中选择一个示例,并讨论以下几点:

  • 如果视频共享服务无法处理指定示例,默认行为是什么?
  • 用户希望在示例中发生什么?
  • 我们可以如何改进此流程?
操作 用户开始上传错误的视频文件。然后,在上传过程中,用户会指定要上传的正确视频文件。
默认情况 原始文件会继续在后台上传,新文件会同时上传。
用户预期 用户希望原始上传会停止,以免浪费额外的互联网带宽。
哪些方面有待改进 在新文件开始上传之前,JavaScript 会取消原始文件的提取请求。
操作 用户在上传视频的过程中失去了互联网连接。
默认情况 上传进度条似乎卡在了 50% 处。最后,Fetch API 发生超时,上传的数据将被舍弃。互联网连接恢复后,用户必须重新上传文件。
用户预期 用户希望在其文件无法上传时收到通知,并且希望在自己恢复联网后自动恢复上传(进度为 50%)。
哪些方面有待改进 上传页面会告知用户互联网连接问题,并向用户保证上传将在互联网连接恢复后继续。
操作 视频分享网站无法处理包含空格的文件名。它使用诸如“My_Travels.mp4”或“MyTravels.mp4”之类的名称,而不是“My Travels.mp4”。
默认情况 用户必须等待上传完成。文件上传后,如果进度条显示为“100%”,进度条会显示以下消息:“请重试。”
用户预期 用户希望在上传开始前或至少在上传后的第一秒内得知文件名限制。
哪些方面有待改进 理想情况下,视频分享服务支持带有空格的文件名。另一种方法是在开始上传之前通知用户存在文件名限制。或者,视频共享服务应拒绝上传并附上详细的错误消息。

使用 Fetch API 处理错误

请注意,以下代码示例使用顶级 await浏览器支持),因为此功能可以简化您的代码。

当 Fetch API 抛出错误时

此示例使用 try/catch语句来捕获 try 块中抛出的任何错误。例如,如果 Fetch API 无法提取指定的资源,则会抛出错误。在这样的 catch 代码块中,请注意提供有意义的用户体验。如果向用户显示了旋转图标(表示某种进度的通用界面),则您可以在 catch 代码块中执行以下操作:

  1. 从页面中移除旋转图标。
  2. 提供有用的消息,说明哪里出了问题,以及用户可以采取哪些选项。
  3. 根据可用选项,向用户显示“重试”按钮。
  4. 在后台,将错误的详细信息发送给您的错误跟踪服务或后端。此操作会记录错误,以便日后进行诊断。
try {
  const response = await fetch('https://website');
} catch (error) {
  // TypeError: Failed to fetch
  console.log('There was an error', error);
}

后续阶段,当您诊断记录的错误时,您可以编写一个测试用例,以便在用户意识到错误之前捕获此类错误。根据错误情况,该测试可以是单元测试、集成测试或验收测试。

当网络状态代码表示出现错误时

此代码示例向始终使用 HTTP 状态代码 429 Too Many Requests 进行响应的 HTTP 测试服务发出请求。有趣的是,响应没有到达 catch 块。404 状态(以及某些其他状态代码)不会返回网络错误,但会正常解析。

如需检查 HTTP 状态代码是否成功,您可以使用以下任一选项:

  • 使用 Response.ok 属性确定状态代码是否在 200299 之间。
  • 使用 Response.status 属性来确定响应是否成功。
  • 使用任何其他元数据(例如 Response.headers)来评估响应是否成功。
let response;

try {
  response = await fetch('https://httpbin.org/status/429');
} catch (error) {
  console.log('There was an error', error);
}

// Uses the 'optional chaining' operator
if (response?.ok) {
  console.log('Use the response here!');
} else {
  console.log(`HTTP Response Code: ${response?.status}`)
}

最佳做法是与贵组织和团队的人员合作,了解可能的 HTTP 响应状态代码。有时,后端开发者、开发者运营和服务工程师可以就您可能未曾预料到的可能极端情况提供独特的见解。

解析网络响应时出错时

此代码示例演示了解析响应正文时可能发生的另一种类型的错误。Response 接口提供了便捷的方法来解析不同类型的数据,例如文本或 JSON。在以下代码中,向 HTTP 测试服务发出网络请求,该服务返回 HTML 字符串作为响应正文。但是,系统会尝试将响应正文解析为 JSON,从而引发错误。

let json;

try {
  const response = await fetch('https://httpbin.org/html');
  json = await response.json();
} catch (error) {
  if (error instanceof SyntaxError) {
    // Unexpected token < in JSON
    console.log('There was a SyntaxError', error);
  } else {
    console.log('There was an error', error);
  }
}

if (json) {
  console.log('Use the JSON here!', json);
}

您必须准备自己的代码以采用多种响应格式,并验证意外响应是否不会破坏用户的网页。

设想以下场景:您有一个返回有效 JSON 响应的远程资源,并且该资源可通过 Response.json() 方法成功解析。服务可能会中断。完成后,系统会返回 500 Internal Server Error。如果在解析 JSON 期间未使用适当的错误处理技术,则可能会给用户破坏页面,因为系统会抛出未处理的错误。

当网络请求必须在完成之前取消时

此代码示例使用 AbortController 取消传输中的请求。传输中的请求是指已开始但尚未完成的网络请求。

您可能需要取消传输中的请求的情况可能不尽相同,但最终取决于您的使用场景和环境。以下代码演示了如何将 AbortSignal 传递给 Fetch API。AbortSignal 附加到 AbortController,并且 AbortController 包含 abort() 方法,告知浏览器应取消网络请求。

const controller = new AbortController();
const signal = controller.signal;

// Cancel the fetch request in 500ms
setTimeout(() => controller.abort(), 500);

try {
  const url = 'https://httpbin.org/delay/1';
  const response = await fetch(url, { signal });
  console.log(response);
} catch (error) {
  // DOMException: The user aborted a request.
  console.log('Error: ', error)
}

总结

处理错误的一个重要方面是定义可能出错的各个部分。对于每种情况,请确保您为用户提供了适当的后备方案。关于提取请求,请思考以下问题:

  • 如果目标服务器发生故障,会发生什么情况?
  • 如果 Fetch 收到意外响应,会发生什么情况?
  • 如果用户的互联网连接失败会怎么样?

根据网页的复杂程度,您还可以草拟流程图,描述不同情景下的功能和界面。