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

本文涉及的产品
Serverless 应用引擎 SAE,800核*时 1600GiB*时
EMR Serverless StarRocks,5000CU*H 48000GB*H
容器服务 Serverless 版 ACK Serverless,952元额度 多规格
简介: 在讨论前端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策略,以确保更好的可靠性。

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

相关文章
|
1天前
|
Web App开发 存储 JavaScript
前端如何学习Node.js及Node.js的主要用途
【7月更文挑战第16天】 学习Node.js对前端开发者至关重要,涉及理解其基于V8的运行时环境、JavaScript基础、安装与验证、核心模块(如fs、http、path)及npm管理。实践项目,如用Express建服务器,参与开源,深入学习异步编程和事件循环。Node.js用于服务器开发、构建工具、本地开发服务器和实时应用,提升全栈能力。借助官方文档和各种资源加速学习。
14 4
|
4天前
|
缓存 JavaScript 前端开发
前端框架与库 - Vue.js基础:模板语法、数据绑定
【7月更文挑战第14天】Vue.js 是渐进式框架,以简洁API和高效数据绑定知名。本文聚焦模板语法与数据绑定,解释常见问题和易错点,助力初学者避坑。模板语法中,{{ expression }} 用于渲染值,v-bind/: 用于动态绑定属性。数据绑定涉及文本、属性和事件,注意v-model适用于表单元素,计算属性有缓存。理解正确用法,借助文档和IDE,可提升开发质量和效率。善用Vue.js,打造响应式UI。
|
3天前
|
JavaScript 前端开发 API
前端框架与库 - Vue.js 组件与路由
【7月更文挑战第15天】Vue.js 框架以简洁API和高效DOM更新著名,组件和路由是构建应用的关键。组件是自包含的实例,常见问题包括命名冲突、作用域混淆和状态管理。要避免这些问题,可使用命名空间、明确数据绑定和事件,以及采用Vuex管理状态。Vue Router提供声明式路由,常见挑战包括路由守卫、动态路由参数和懒加载配置。正确使用路由守卫、处理动态参数和实现代码分割能优化路由管理。提供的代码示例展示了基本组件和路由配置。
|
13天前
|
JSON JavaScript 前端开发
若依修改,若依如何发送get和post请求,发送数据请求的写法,若依请求的API在src的api文件下,建立请求的第一步,在API中新建一个文件,第二步新建JavaScript文件
若依修改,若依如何发送get和post请求,发送数据请求的写法,若依请求的API在src的api文件下,建立请求的第一步,在API中新建一个文件,第二步新建JavaScript文件
|
13天前
|
前端开发 NoSQL 数据库
部署常用的流程,可以用后端,连接宝塔,将IP地址修改好,本地只要连接好了,在本地上前后端跑起来,前端能够跑起来,改好了config.js资料,后端修改好数据库和连接redis,本地上跑成功了,再改
部署常用的流程,可以用后端,连接宝塔,将IP地址修改好,本地只要连接好了,在本地上前后端跑起来,前端能够跑起来,改好了config.js资料,后端修改好数据库和连接redis,本地上跑成功了,再改
|
3天前
|
存储 资源调度 前端开发
JavaScript 使用axios库发送 post请求给后端, 给定base64格式的字符串数据和一些其他参数, 使用表单方式提交, 并使用onUploadProgress显示进度
使用 Axios 发送包含 Base64 数据和其他参数的 POST 请求时,可以通过 `onUploadProgress` 监听上传进度。由于整个请求体被视为一个单元,所以进度可能不够精确,但可以模拟进度反馈。前端示例代码展示如何创建一个包含 Base64 图片数据和额外参数的 `FormData` 对象,并在上传时更新进度条。后端使用如 Express 和 Multer 可处理 Base64 数据。注意,实际进度可能不如文件上传精确,显示简单加载状态可能更合适。
|
9天前
|
XML 前端开发 JavaScript
JavaScript进阶 - AJAX请求与Fetch API
【7月更文挑战第9天】JavaScript进阶:AJAX与Fetch API对比。AJAX用于异步数据交换,XMLHttpRequest API复杂,依赖回调。Fetch API是现代、基于Promise的解决方案,简化请求处理。示例:`fetch(&#39;url&#39;).then(r =&gt; r.json()).then(data =&gt; console.log(data)).catch(err =&gt; console.error(err))`。注意点包括检查HTTP状态、错误处理、CORS、Cookie和超时。Fetch提高了异步代码的可读性,但需留意潜在问题。
|
13天前
|
JavaScript API
若依没解构送post请求出现的bug,vue.runtime.esm.js:620 [Vue warn]: Error in mounted hook: “TypeError: (0 , _inde
若依没解构送post请求出现的bug,vue.runtime.esm.js:620 [Vue warn]: Error in mounted hook: “TypeError: (0 , _inde
|
21天前
|
前端开发 JavaScript 数据库
如何实现前后端分离-----前端笔记
如何实现前后端分离-----前端笔记
|
21天前
|
前端开发 安全 NoSQL
技术笔记:Security前端页面配置
技术笔记:Security前端页面配置