当前位置: 首页 > news >正文

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

引言

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.mrgr.cn/news/59562.html

相关文章:

  • 【verilog】四位全加器
  • 如何对群辉docker进行简单更新升级
  • Zypher Network Layer3 主网上线,“宝藏方舟”活动是亮点
  • python的Django的render_to_string函数和render函数模板的使用
  • 开源呼叫中心系统与商业软件的对比
  • 旧电脑安装Win11提示“这台电脑当前不满足windows11系统要求”,安装中断。怎么办?
  • static、 静态导入、成员变量的初始化、单例模式、final 常量(Content)、嵌套类、局部类、抽象类、接口、Lambda、方法引用
  • 2024 BuildCTF 公开赛|MISC
  • Redis高频面试题
  • 【C++】—— 模板进阶
  • 十三、层次式架构设计理论与实践
  • 为制造业挑选CRM?11款软件对比指南
  • spring高手之路
  • 使用沉浸式翻译插件来使用多种人工智能工具翻译网页上的某段文字,如何做?
  • yolov5将推理模型导出为onnx
  • 字节青训营 红包运气排行榜
  • 初始JavaEE篇——多线程(4):生产者-消费者模型、阻塞队列
  • 【无人机设计与控制】改进人工势场法,引入模糊控制实现无人机路径规划和避障
  • mongodb:增删改查和特殊查询符号手册
  • 探索Python安全字符串处理的奥秘:MarkupSafe库揭秘
  • 轻松构建高效 API:FastAPI 的主要特点与实战应用20241027
  • Spring Boot技术在学生宿舍管理系统中的创新
  • 【工具使用】VSCode如何将本地项目关联到远程的仓库 (vscode本地新项目与远程仓库建立链接)
  • C语言初阶:十.结构体基础
  • 使用 Docker 管理完整项目:Java、Vue、Redis 和 Nginx 的一站式部署
  • ubuntu20上部署gitlab并开启ipv6访问