使用 Fetch API 时实现错误处理

Umar Hansa
Umar Hansa

本文演示了使用 Fetch API 时的某些错误处理方法。借助 Fetch API,您可以向远程网络资源发出请求。当您进行远程网络调用时,您的网页可能会出现各种潜在的网络错误。

以下部分介绍了可能出现的错误,并介绍了如何编写代码,以提供合理的功能级别,使其能够对错误和意外的网络状况具有弹性。弹性代码可让用户满意,并为您的网站提供标准服务。

本部分介绍了以下场景:用户创建名为 "My Travels.mp4" 的新视频,然后尝试将该视频上传到视频分享网站。

使用 Fetch 时,很容易考虑用户成功上传视频的正常路径。不过,还有一些路径不太顺畅,但 Web 开发者必须为此做好规划。此类不理想的路径可能是由用户错误、意外的环境条件或视频分享网站上的 bug 导致的。

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

环境变化示例

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

视频共享网站出现错误的示例

  • 视频分享网站无法处理带空格的文件名。它预期使用 "My_Travels.mp4""MyTravels.mp4" 等名称,而不是 "My Travels.mp4"
  • 视频分享网站无法上传超出接受的最大文件大小的视频。
  • 视频共享网站不支持上传视频中的视频编解码器。

这些示例在现实生活中可能发生,也确实发生过。您可能过去就遇到过这样的示例!我们从前面各个类别中各选一个示例,并讨论以下几点:

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

使用 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 测试服务发出请求,该服务始终响应 HTTP 状态代码 429 Too Many Requests。有趣的是,响应未到达 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)
}

总结

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

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

根据网页的复杂程度,您还可以绘制一个流程图,描述不同场景的功能和界面。