关于浏览器请求队列和超时表现(canceled)

2024-08-23 01:18

本文主要是介绍关于浏览器请求队列和超时表现(canceled),希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

前端在向服务器 API 发送请求时一般会设置一个超时时间,避免超过期望时间的持续等待。

以 Axios 为例,一般会设置 timeout 请求超时选项。

但是浏览器判断超时并不是这么简单。

搭建环境

express + axios 搭建 web 服务。

在项目目录下安装依赖:npm i express axios

添加文件:

// start.js
const express = require('express')const PORT = 3000let app = express()app.use(express.static(path.join(__dirname, 'public')))
app.use('/api', require('./timeout'))app.listen(PORT, () => {console.log(`Sever listening on port ${PORT}`)
})
// timeout.js
const express = require('express');const router = express.Router()router.get('/timeout', async (req, res) => {// 用于查看服务器接收到请求的时机console.log('timeout')// 5 秒后响应setTimeout(() => {res.status(200).end('OK')}, 5000);
})module.exports = router
<!-- public/index.html -->
<!DOCTYPE html>
<html lang="en"><head><meta charset="UTF-8" /><script src="https://unpkg.com/axios@0.24.0/dist/axios.min.js"></script></head><body><script>const instance = axios.create({timeout: 1000 // 超过1秒超时,中止请求})function fetch() {instance.get('/api/timeout')}fetch()</script></body>
</html>

运行 node start.js,访问 http://localhost:3000

正常超时

正常情况下,浏览器发送请求,超过设置的时间后,axios 就会主动中断请求。

F12 开发人员工具的网络面板中可以看到,请求状态为 canceled(已取消)。

在这里插入图片描述

竞态场景

实际场景下,页面中经常会有多个请求同时发起,而不同浏览器也会限制同一时间处理请求的最大数量。这就会产生竞态,首先发起的请求就会被优先处理。超过上限的请求会强制挂起(pending),当前面的请求接收响应移出队列,腾出位置,挂起的请求才允许发送给服务器。

关键在于,请求挂起的过程中消耗的时间仍参与超时时间计算。

这就会有一些无法预料的超时场景,例如:

  • A请求耗时长,B在等待A的时候消耗过多时间,等到处理B请求的时候,时间所剩无几,还没等服务器响应,浏览器就主动中断了B请求
  • A请求耗时长,B在等待A的时候达到超时时间,浏览器中止B请求,可怜B请求的服务器什么都还没做
  • 等等

相同地址竞态

同一时间,浏览器只会处理同一地址的一个请求,其它的会挂起。

// public/index.html
const instance = axios.create({timeout: 8000 // 超过8秒超时,中止请求
})function fetch() {instance.get('/api/timeout')
}// 同时发起两个相同地址的请求
fetch()
fetch()

同时发起两个相同地址的请求,仅处理第一个,后面的会挂起,等待前面的请求结束

在这里插入图片描述

5秒后(如果只考虑服务器处理的时间)第一个接收响应(处理完成),轮到第二个,此时第二个的请求时间已经消耗了5秒

在这里插入图片描述

达到8秒时间上限,浏览器中止第二个请求。

在这里插入图片描述

同源不同地址竞态

浏览器允许同域名下多个不同地址的并发请求,但也设置了数量上限,例如 Chrome 上限为 6 个。

添加多个不同地址的5秒响应接口:

const express = require('express');const router = express.Router()router.get('/timeout', async (req, res, next) => {console.log('timeout')// 5 秒后响应setTimeout(() => {res.status(200).end('OK')}, 5000);
})router.get('/timeout2', async (req, res, next) => {console.log('timeout2')setTimeout(() => {res.status(200).end('OK')}, 5000);
})router.get('/timeout3', async (req, res, next) => {console.log('timeout3')setTimeout(() => {res.status(200).end('OK')}, 5000);
})router.get('/timeout4', async (req, res, next) => {console.log('timeout4')setTimeout(() => {res.status(200).end('OK')}, 5000);
})router.get('/timeout5', async (req, res, next) => {console.log('timeout5')setTimeout(() => {res.status(200).end('OK')}, 5000);
})router.get('/timeout6', async (req, res, next) => {console.log('timeout6')setTimeout(() => {res.status(200).end('OK')}, 5000);
})router.get('/timeout7', async (req, res, next) => {console.log('timeout7')setTimeout(() => {res.status(200).end('OK')}, 5000);
})router.get('/timeout8', async (req, res, next) => {console.log('timeout8')setTimeout(() => {res.status(200).end('OK')}, 5000);
})module.exports = router
// public/index.html
const instance = axios.create({timeout: 8000 // 超过8秒超时,中止请求
})function fetch(num) {instance.get('/api/timeout' + (num || ''))
}// 同一时间发送多个不同地址的请求
['',2,3,4,5,6,7,8].forEach(v => fetch(v))

同一时间向同域名下不同地址的接口发送请求,最多处理6个请求,其它的全部挂起

在这里插入图片描述

5秒后(如果只考虑服务器处理的时间)优先处理的6个请求接收响应(处理完成),挂起的请求进行补位,此时剩余接口的请求时间已经消耗了5秒

在这里插入图片描述

达到8秒时间上限,浏览器中止超时的请求。

在这里插入图片描述

不同源下地址竞态

浏览器对不同源下的请求不进行竞态,这就是使用 CDN 提高资源并行加载效率的原因。这里就不做示例了。

关于同源参考:浏览器的同源策略

总结

  • 前端设置的请求超时时间是从 JS 发起请求开始计算
  • 浏览器同一时间处理请求有数量上限,超过上限的请求将被挂起,等待处理队列空闲才会依次处理剩下的请求
  • 浏览器挂起的请求依然参与超时时间计算
  • 为避免请求在真正发送到服务器之前被浏览器消耗时间,可以使用 CDN 等不同源的请求地址,或定制超时重试机制。

这篇关于关于浏览器请求队列和超时表现(canceled)的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1097942

相关文章

SpringBoot使用OkHttp完成高效网络请求详解

《SpringBoot使用OkHttp完成高效网络请求详解》OkHttp是一个高效的HTTP客户端,支持同步和异步请求,且具备自动处理cookie、缓存和连接池等高级功能,下面我们来看看SpringB... 目录一、OkHttp 简介二、在 Spring Boot 中集成 OkHttp三、封装 OkHttp

使用DrissionPage控制360浏览器的完美解决方案

《使用DrissionPage控制360浏览器的完美解决方案》在网页自动化领域,经常遇到需要保持登录状态、保留Cookie等场景,今天要分享的方案可以完美解决这个问题:使用DrissionPage直接... 目录完整代码引言为什么要使用已有用户数据?核心代码实现1. 导入必要模块2. 关键配置(重点!)3.

Go语言中最便捷的http请求包resty的使用详解

《Go语言中最便捷的http请求包resty的使用详解》go语言虽然自身就有net/http包,但是说实话用起来没那么好用,resty包是go语言中一个非常受欢迎的http请求处理包,下面我们一起来学... 目录安装一、一个简单的get二、带查询参数三、设置请求头、body四、设置表单数据五、处理响应六、超

Qt实现发送HTTP请求的示例详解

《Qt实现发送HTTP请求的示例详解》这篇文章主要为大家详细介绍了如何通过Qt实现发送HTTP请求,文中的示例代码讲解详细,具有一定的借鉴价值,感兴趣的小伙伴可以跟随小编一起学习一下... 目录1、添加network模块2、包含改头文件3、创建网络访问管理器4、创建接口5、创建网络请求对象6、创建一个回复对

Spring Boot整合消息队列RabbitMQ的实现示例

《SpringBoot整合消息队列RabbitMQ的实现示例》本文主要介绍了SpringBoot整合消息队列RabbitMQ的实现示例,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的... 目录RabbitMQ 简介与安装1. RabbitMQ 简介2. RabbitMQ 安装Spring

SpringBoot项目注入 traceId 追踪整个请求的日志链路(过程详解)

《SpringBoot项目注入traceId追踪整个请求的日志链路(过程详解)》本文介绍了如何在单体SpringBoot项目中通过手动实现过滤器或拦截器来注入traceId,以追踪整个请求的日志链... SpringBoot项目注入 traceId 来追踪整个请求的日志链路,有了 traceId, 我们在排

Java中实现订单超时自动取消功能(最新推荐)

《Java中实现订单超时自动取消功能(最新推荐)》本文介绍了Java中实现订单超时自动取消功能的几种方法,包括定时任务、JDK延迟队列、Redis过期监听、Redisson分布式延迟队列、Rocket... 目录1、定时任务2、JDK延迟队列 DelayQueue(1)定义实现Delayed接口的实体类 (

Nginx设置连接超时并进行测试的方法步骤

《Nginx设置连接超时并进行测试的方法步骤》在高并发场景下,如果客户端与服务器的连接长时间未响应,会占用大量的系统资源,影响其他正常请求的处理效率,为了解决这个问题,可以通过设置Nginx的连接... 目录设置连接超时目的操作步骤测试连接超时测试方法:总结:设置连接超时目的设置客户端与服务器之间的连接

如何使用Java实现请求deepseek

《如何使用Java实现请求deepseek》这篇文章主要为大家详细介绍了如何使用Java实现请求deepseek功能,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起学习一下... 目录1.deepseek的api创建2.Java实现请求deepseek2.1 pom文件2.2 json转化文件2.2

Android 悬浮窗开发示例((动态权限请求 | 前台服务和通知 | 悬浮窗创建 )

《Android悬浮窗开发示例((动态权限请求|前台服务和通知|悬浮窗创建)》本文介绍了Android悬浮窗的实现效果,包括动态权限请求、前台服务和通知的使用,悬浮窗权限需要动态申请并引导... 目录一、悬浮窗 动态权限请求1、动态请求权限2、悬浮窗权限说明3、检查动态权限4、申请动态权限5、权限设置完毕后