fetch: 取消请求、读取流、获取下载进度...

news/2024/11/1 17:19:11/
webkit-tap-highlight-color: rgba(0, 0, 0, 0);">

引言

Fetch API 提供了一个获取资源的接口(包括跨网络通信)。对于任何使用过 XMLHttpRequest 的开发者来说, 对于 Fetch 应该都能轻松上手, 而且新的 API 提供了更强大和灵活的功能集…

本文主要就是记录下, 在使用 Fetch 期间可能会碰到的几个小案例…

一、取消请求

前端开发中, 取消请求是一个比较常见的需求了吧!!

下面举个场景, 比如我们要实现一个类似 google 一样的搜索提示下拉框, 下拉框内容是根据输入框内容查询出来的!! 当用户快速输入词条, 必然会高频的调用接口, 这时难免会出现先请求的接口响应速度比后请求的慢, 这样的话就有可能出现响应覆盖的问题!! 这里常规的解决办法有以下几种:

  1. 防抖: 用户快速地交互过程中, 只使用最后一次交互产生的数据, 然后再发起请求!
  2. 锁状态: 在上一个接口没有返回数据时, 交互状态一直处于 loading 的锁定状态
  3. 取消上一个请求: 在发起下一个请求前, 把之前的请求取消掉

防抖锁状态 虽然能解决问题, 但或多或少还是会影响用户的一个体验, 所以个人认为比较好的方案就是 取消上一个请求。下面将介绍如果在 Fetch 中如何取消请求!

Fetch 中如果需要中止未完成请求, 可使用 AbortController, AbortController 接口表示一个控制器对象, 允许你根据需要中止一个或多个 Web 请求

具体使用见下面代码:

  • 先创建了一个 AbortController 实例对象 controller
  • fetch 发起请求过程中, 设置 signal 参数, 让对应 fetch 请求和 AbortController 控制器进行一个绑定
  • 通过定时器, 在 1 秒后通过控制器的 abort() 方法来取消所有和控制器相互关联的请求
  • 最后我们可以在 .catch 中通过判断 Error 对象的 name 属性, 来检测到请求取消的行为
const controller = new AbortController();fetch("/user/12345", { signal: controller.signal }
).then(response => {console.log('请求成功');
}).catch(err => {if(err.name === "AbortError") {// 请求被手动取消} else {// 处理正常错误}
});// 1S 后手动取消请求
setTimeout(() => {controller.abort();
}, 1000)

二、读取流数据

想必你应该听说过 Server-Sent Events(SSE), 如果还不清楚可以看看我的这篇文章 《在 Koa 中基于 gpt-3.5 模型实现一个最基本的流式问答 DEMO》!!

本质上其实就是后端接口和前端建立了一个单向长连接, 然后后端不断的向前端推送流数据, 前端可通过 SSE 的方式来接收流数据!!

但实际上, 在 Fetch 中其实也是支持实时读取流数据的, 我们可以使用 response.body 属性。它是 ReadableStream 特殊对象, 它允许接口逐块(chunk)为前端提供 body。在 Streams API 规范中有对 ReadableStream 的详细描述!

如下代码所示:

  • await reader.read(): 调用的结果是一个具有两个属性的对象
  • done: 当读取完成时为 true, 否则为 false
  • value: 字节的类型化数组: Uint8Array
const handle = async () => {// 1. 请求接口const response = await fetch('http://127.0.0.1:4000/demo');const reader = response.body.getReader(); // 获取readerconst decoder = new TextDecoder(); // 文本解码器// 2. 循环取值while (true) {// 取值, value 是后端返回流信息, done 表示后端结束流的输出const { value, done } = await reader.read();if (done) break;// 打印值: 对 value 进行解码console.log('推送数据', decoder.decode(value));}
};handle();

三、获取下载文件长度

上文提到 Fetch 可以分片读取流数据, 那么如果我们能够知道要获取的资源大小或者长度, 那么我们就能够通过资源大小以及获取到的数据大小来计算出请求的进度

所以这里其实还需要后端配合, 需要将响应头 Content-Length 设置为资源的一个完整的长度, 这样前端就可以直接通过响应头 Content-Length 来拿到资源的完整长度, 从而计算出当前下载进度

// 1. 启动 fetch, 并获得一个 reader
let response = await fetch('https://api.github.com/repos/javascript-tutorial/en.javascript.info/commits?per_page=100');
const reader = response.body.getReader();// 2. 获得总长度(length)
const contentLength = +response.headers.get('Content-Length');// 3. 读取数据
let receivedLength = 0; // 当前接收到了这么多字节
let chunks = []; // 接收到的二进制块的数组(包括 body)while(true) {const {done, value} = await reader.read();if (done) {break;}chunks.push(value);receivedLength += value.length;console.log(`Received ${receivedLength} of ${contentLength}`)
}// 4. 将块连接到单个 Uint8Array
let chunksAll = new Uint8Array(receivedLength); // (4.1)
let position = 0;
for(let chunk of chunks) {chunksAll.set(chunk, position); // (4.2)position += chunk.length;
}// 5. 解码成字符串
let result = new TextDecoder("utf-8").decode(chunksAll);// 我们完成啦!
let commits = JSON.parse(result);
alert(commits[0].author.login);

四、上传文件进度条

到目前为止, fetch 方法无法跟踪 上传 进度。相关功能可以使用 XMLHttpRequest 来实现

使用 XMLHttpRequest 对象, 它提供了一个 progress 事件, 该事件在 上传数据时 会不断被触发, 并且在回调函数中我们可以获取当当前上传的一个进度, 具体参考 《MDN - 使用 XMLHttpRequest》

var oReq = new XMLHttpRequest();oReq.addEventListener("progress", updateProgress);
oReq.addEventListener("load", transferComplete);
oReq.addEventListener("error", transferFailed);
oReq.addEventListener("abort", transferCanceled);oReq.open();// ...// 服务端到客户端的传输进程(下载)
function updateProgress(oEvent) {if (oEvent.lengthComputable) {var percentComplete = (oEvent.loaded / oEvent.total) * 100;// ...} else {// 总大小未知时不能计算进程信息}
}function transferComplete(evt) {console.log("The transfer is complete.");
}function transferFailed(evt) {console.log("An error occurred while transferring the file.");
}function transferCanceled(evt) {console.log("The transfer has been canceled by the user.");
}

五、参考

  • Fetch: 下载进度
  • fetch 下载文件 + 显示进度
  • axios/fetch/XMLHttpRequest怎么取消请求

image


http://www.ppmy.cn/news/1543636.html

相关文章

YOLOv6-4.0部分代码阅读笔记-loss_fuseab.py

loss_fuseab.py yolov6\models\losses\loss_fuseab.py ‌YOLOv6中的 loss_distill_ns 、 loss_distill 、 loss_fuseab 和 loss 的区别主要在于它们的应用场景、功能和目的。‌ ‌loss_distill_ns : ‌应用场景‌ :主要用于小模型,如YOLOv…

线性代数群论应用:正逆运动学 变换矩阵

在机器人学中,为了描述机器人的运动,将机器人建模为正运动学和逆运动学 正运动学:从机器人的关节空间描述计算笛卡尔空间描述的机器人末端执行器的位置和姿态,该问题通常是一个几何问题,给定一组关节角度,…

Vue将所展示文本内容的换行与空格显示出来

使用<pre>标签 <pre>{{ content }}</pre>设置white-space样式&#xff08;推荐&#xff09; <div class"content">{{ content }} </div> .content{white-space: pre-wrap; }菜鸟教程&#xff1a;white-space: pre-wrap;的用途在于它…

QT-使用QSS美化UI界面

一、QSS简介&#xff1a; Qt Style Sheet&#xff1a;Qt样式表&#xff0c;用来自定义控件外观的一种机制&#xff0c;可以把他类比成CSS&#xff08;CSS主要功能与最终目的都是能使界面的表现与界面的元素分离&#xff09;。QSS机制使应用程序也能像web界面那样随意地改变外观…

瑞芯微 RKNN SDK 快速上手指南--Banana Pi开源社区

此文档面向零基础用户详细介绍如何快速在计算机上使用 RKNN-Toolkit2 完成模型转换&#xff0c;并通过 RKNPU2 部署到 Rockchip 开发板上。本文所用示例已集成到 RKNN Model Zoo 中。 支持的平台&#xff1a;RK3562、RK3566系列、RK3568系列、RK3576系列、RK3588系列 开发板&a…

H2 Database IDEA 源码 DEBUG 环境搭建

H2 Database IDEA 源码 DEBUG 环境搭建 基于最新的 version-2.3.230 拉取分支。 git remote add h2 https://github.com/h2database/h2database.git git fetch h2 git checkout -b version-2.3.230 version-2.3.230使用 # 启动 java -jar h2*.jar# H2 shell 方式使用 java …

如何使用java雪花算法在分布式环境中生成唯一ID?

引言 在现代分布式系统中,生成唯一标识符(ID)是一个常见的需求。传统的自增ID在分布式环境中会导致冲突,因此需要一种能够在分布式系统中生成全局唯一ID的算法。 雪花算法(Snowflake)就是为了解决这个问题而提出的一种高效的ID生成算法。本文将详细介绍雪花算法的原理、…

qt QAction详解

1、概述 QAction是Qt框架中的一个抽象类&#xff0c;用于表示用户界面中的一个动作&#xff08;action&#xff09;。这些动作可以绑定到菜单项、工具栏按钮或快捷键上&#xff0c;提供了一种灵活的方式来处理用户交互。QAction不仅包含了动作的名称、图标、提示信息等属性&am…