前端JS发起的请求能暂停吗?

本文涉及的产品
性能测试 PTS,5000VUM额度
注册配置 MSE Nacos/ZooKeeper,118元/月
服务治理 MSE Sentinel/OpenSergo,Agent数量 不受限
简介: 在讨论前端JS发起的请求是否能暂停时,需要明确两个概念:什么状态可以被认为是“暂停”?以及什么是JS发起的请求?

在讨论前端JS发起的请求是否能暂停时,需要明确两个概念:什么状态可以被认为是“暂停”?以及什么是JS发起的请求?

如何定义暂停?

暂停指的是临时停止一个已经开始但尚未完成的过程。这意味着这个过程可以在某个时间点被中断,并在另一个时间点恢复。

什么是请求?

首先,让我们介绍一下TCP/IP网络模型。网络模型从上到下分为应用层、传输层、网络层和网络接口层。

新建项目 (3).jpg
上图表示,每次网络传输,应用数据都需要通过网络模型逐层打包,然后发送到目的地,就像寄包裹一样。要寄送的物品首先被包装并登记其大小,然后放入箱子并登记目的地,最后装上运输工具送到目的地。

请求的概念可以理解为客户端通过多次数据网络传输将完整数据发送到服务器,而服务器为特定请求返回的数据可以称为响应。

理论上,应用层协议可以通过标记数据包序列号来实现暂停机制。然而,TCP协议不支持这一点。TCP协议的数据传输是面向流的,数据被视为连续的字节流。客户端发送的数据将被分成多个独立传输的TCP段。无法直接控制每个TCP段的传输,因此无法实现暂停请求或响应的功能。

WX20240614-110504@2x.png

回答问题

如果请求指的是网络模型中的传输,那么自然是不可能暂停的。

考虑到使用场景——由JS发起的请求。因此,可以认为这里的问题指的是在JS运行时发起的XMLHttpRequest或fetch请求。由于请求已经发出,问题自然变成响应是否可以暂停。

我们都知道,上传大文件分片和下载大文件本质上是定义分片顺序,按顺序请求,可以通过中断和记录中断点来实现暂停和恢复。然而,单个请求并没有这样的环境。

使用JS实现“假暂停”机制

虽然我们无法真正实现暂停请求,但我们可以模拟一个假暂停功能。在前端业务场景中,数据在接收到后不会立即显示在客户端。前端开发人员需要先处理这些数据,然后再渲染到界面上。如果我们在发起请求前添加一个控制器,并且在请求返回时该控制器处于暂停状态,则不处理数据。相反,等待控制器恢复后再处理数据。这样我们是否就达到了目标呢?让我们尝试实现它。

如果我们使用 fetch 发起请求,可以设计一个控制器 Promise,并结合请求使用 Promise.all 封装。当 fetch 完成时,检查控制器是否处于暂停状态;如果没有暂停,直接resolve 控制器并同时 resolve 和抛出 Promise.all。

function _request () {
   
   
  return new Promise<number>((res) => setTimeout(() => {
   
   
    res(123)
  }, 3000))
}

// 原本想用 "class extends Promise" 实现。
// 问题在于https://github.com/nodejs/node/issues/13678。
function createPauseControllerPromise () {
   
   
  const result = {
   
   
    isPause: false,
    resolveWhenResume: false,
    resolve (value?: any) {
   
   },
    pause () {
   
   
      this.isPause = true
    },
    resume () {
   
   
      if (!this.isPause) return
      this.isPause = false
      if (this.resolveWhenResume) {
   
   
          this.resolve()
      }
    },
    promise: Promise.resolve()
  }

  const promise = new Promise<void>((res) => {
   
   
    result.resolve = res
  })

  result.promise = promise

  return result
}

function requestWithPauseControl <T extends () => Promise<any>>(request: T) {
   
   
  const controller = createPauseControllerPromise()

  const controlRequest = request().then((data) => {
   
   
      if (!controller.isPause) controller.resolve()
      controller.resolveWhenResume = controller.isPause
      return data
  })

  const result = Promise.all([controlRequest, controller.promise])
      .then(data => data[0])

  result.finally(() => controller.resolve())

  (result as any).pause = controller.pause.bind(controller);
  (result as any).resume = controller.resume.bind(controller);

  return result as ReturnType<T> & {
   
    pause: () => void, resume: () => void }
}

使用方法

我们可以将调用 _request 替换为调用 requestWithPauseControl(_request) ,并通过返回的pause和 resume 方法控制暂停和恢复。

const result = requestWithPauseControl(_request).then((data) => {
   
   
    console.log(data)
})

if (Math.random() > 0.5) {
   
    result.pause() }

setTimeout(() => {
   
   
    result.resume()
}, 4000)

执行原理

在流程设计上,步骤如下:设计一个控制器,发起请求,在接收到响应后,检查控制器的状态。如果控制器不处于“暂停”状态,则正常返回数据;如果控制器处于“暂停”状态,则将控制器设置为一旦调用resume方法就返回数据的状态。

在代码中,使用 Promise.all 将控制器 Promise 绑定。如果控制器处于暂停状态,Promise.all 不会被释放。然后对应地暴露 pause 方法和 resume 方法供外部使用。

结语

有些人可能误以为网络请求和响应根本无法暂停。我在文章开头特别提到了数据传输相关的内容,并加了一句“理论上,应用层协议可以通过标记数据包序列号来实现暂停机制”。这意味着,如果你修改HTTP或设计自己的应用层协议(例如socket、vmess等协议),只要双方支持该协议,就可以实现请求或响应的暂停。这不会影响TCP连接,但实现暂停机制需要综合考虑场景和TCP策略,以确保更好的可靠性。

例如,提供一种控制消息类型来控制传输暂停,需要标记所有数据包的序列号。当需要暂停时,发送带有序列号的暂停消息到接收端。接收端收到暂停消息后,将已收到的数据包标记块返回给发送端(类似分片上传机制)。

相关文章
|
25天前
|
JavaScript 前端开发 程序员
前端原生Js批量修改页面元素属性的2个方法
原生 Js 的 getElementsByClassName 和 querySelectorAll 都能获取批量的页面元素,但是它们之间有些细微的差别,稍不注意,就很容易弄错!
|
22天前
|
JavaScript 前端开发 Java
springboot解决js前端跨域问题,javascript跨域问题解决
本文介绍了如何在Spring Boot项目中编写Filter过滤器以处理跨域问题,并通过一个示例展示了使用JavaScript进行跨域请求的方法。首先,在Spring Boot应用中添加一个实现了`Filter`接口的类,设置响应头允许所有来源的跨域请求。接着,通过一个简单的HTML页面和jQuery发送AJAX请求到指定URL,验证跨域请求是否成功。文中还提供了请求成功的响应数据样例及请求效果截图。
springboot解决js前端跨域问题,javascript跨域问题解决
|
24天前
|
缓存 JavaScript 前端开发
JavaScript 与 DOM 交互的基础及进阶技巧,涵盖 DOM 获取、修改、创建、删除元素的方法,事件处理,性能优化及与其他前端技术的结合,助你构建动态交互的网页应用
本文深入讲解了 JavaScript 与 DOM 交互的基础及进阶技巧,涵盖 DOM 获取、修改、创建、删除元素的方法,事件处理,性能优化及与其他前端技术的结合,助你构建动态交互的网页应用。
38 5
|
22天前
|
缓存 前端开发 JavaScript
JavaScript前端路由的实现原理及其在单页应用中的重要性,涵盖前端路由概念、基本原理、常见实现方式
本文深入解析了JavaScript前端路由的实现原理及其在单页应用中的重要性,涵盖前端路由概念、基本原理、常见实现方式(Hash路由和History路由)、优点及挑战,并通过实际案例分析,帮助开发者更好地理解和应用这一关键技术,提升用户体验。
57 1
|
26天前
|
JSON 前端开发 JavaScript
聊聊 Go 语言中的 JSON 序列化与 js 前端交互类型失真问题
在Web开发中,后端与前端的数据交换常使用JSON格式,但JavaScript的数字类型仅能安全处理-2^53到2^53间的整数,超出此范围会导致精度丢失。本文通过Go语言的`encoding/json`包,介绍如何通过将大整数以字符串形式序列化和反序列化,有效解决这一问题,确保前后端数据交换的准确性。
33 4
|
1月前
|
移动开发 前端开发 JavaScript
前端实训,刚入门,我用原生技术(H5、C3、JS、JQ)手写【网易游戏】页面特效
于辰在大学期间带领团队参考网易游戏官网的部分游戏页面,开发了一系列前端实训作品。项目包括首页、2021校园招聘页面和明日之后游戏页面,涉及多种特效实现,如动态图片切换和人物聚合效果。作品源码已上传至CSDN,视频效果可在CSDN预览。
33 0
前端实训,刚入门,我用原生技术(H5、C3、JS、JQ)手写【网易游戏】页面特效
|
25天前
|
JavaScript 前端开发
JavaScript中的原型 保姆级文章一文搞懂
本文详细解析了JavaScript中的原型概念,从构造函数、原型对象、`__proto__`属性、`constructor`属性到原型链,层层递进地解释了JavaScript如何通过原型实现继承机制。适合初学者深入理解JS面向对象编程的核心原理。
25 1
JavaScript中的原型 保姆级文章一文搞懂
|
5月前
|
JavaScript Java 测试技术
基于springboot+vue.js+uniapp的客户关系管理系统附带文章源码部署视频讲解等
基于springboot+vue.js+uniapp的客户关系管理系统附带文章源码部署视频讲解等
102 2
|
22天前
JS+CSS3文章内容背景黑白切换源码
JS+CSS3文章内容背景黑白切换源码是一款基于JS+CSS3制作的简单网页文章文字内容背景颜色黑白切换效果。
17 0
|
5月前
|
JavaScript Java 测试技术
基于springboot+vue.js+uniapp的小区物流配送系统附带文章源码部署视频讲解等
基于springboot+vue.js+uniapp的小区物流配送系统附带文章源码部署视频讲解等
142 4