UzumakiItachi
首页
  • JavaSript
  • Vue

    • Vue2
    • Vue3
  • React

    • React_18
  • WebPack
  • 浏览器相关
  • 工程化相关
  • 工作中遇到的问题以及解决方案
  • Git
  • 面试
  • 学习
  • 心情杂货
  • 实用技巧
  • 友情链接
关于
  • 个人产出
  • 实用工具
  • 分类
  • 标签
  • 归档
GitHub (opens new window)

UzumakiItachi

起风了,唯有努力生存。
首页
  • JavaSript
  • Vue

    • Vue2
    • Vue3
  • React

    • React_18
  • WebPack
  • 浏览器相关
  • 工程化相关
  • 工作中遇到的问题以及解决方案
  • Git
  • 面试
  • 学习
  • 心情杂货
  • 实用技巧
  • 友情链接
关于
  • 个人产出
  • 实用工具
  • 分类
  • 标签
  • 归档
GitHub (opens new window)
  • WebPack

  • 浏览器相关

  • 工程化相关

  • 工作中遇到的问题以及解决方案

    • 解决浏览器返回页面不刷新的问题
    • 前端如何下载文件流
    • uniapp APP端实现更新最新安装包
    • GitHub Actions Process completed with exit code 128 的解决方案
    • 保留文字输入的空格和换行
    • 缩放适配大屏页
    • 一次性加载n多张图片的性能优化方案
    • NOT-Cool 低代码页面架构思路
    • h5下载vcard快捷保存联系人信息
    • 结合elementui实现的动态主题
    • 解决monorepo场景下子包作为依赖项在开发和生产暴露文件的问题
    • 给npm配置github令牌
    • 移动端电子印章解决方案
    • 小程序canvas绘制海报中遇到的一些坑
    • 超详细的虚拟列表实现过程
    • 小程序实现一个事件中心
    • 请求超时后如何优雅的重新请求
    • 超详细的大文件上传实现方案
    • 仿抖音短视频组件实现方案
    • swiper渲染大量数据的优化方案
    • 前端检测更新,自动刷新网页
  • Git

  • Vite

  • 一些小工具

  • 算法

  • 服务器

  • HTTP

  • 技术
  • 工作中遇到的问题以及解决方案
hanhanbuku
2024-12-06

请求超时后如何优雅的重新请求

在日常工作中经常会看到请求超时这个概念,有时候网络慢或者其他异常原因导致一个请求迟迟得不到响应,我们就需要对这一异常情况做处理。通常的做法就是设置一个超时时间,一旦超过了这个时间还没有响应就视为超时并且取消掉这个请求然后捕获这个错误。 那么在不借助第三方库的情况下我们该如何自己实现一个请求超时并重新发起请求呢?

  1. 设计逻辑

答

这里我想要实现一个请求超时了就取消当前超时的请求,并且重新发起一个请求,最多重复3次。如果3次都超时则返回请求失败,下面就来一步一步去实现一下这个工具函数

  1. 首先我们实现一个最基础的请求函数
/**
 * 发送fetch请求
 * @param url 请求路径
 * @param config fetch配置
 */
const sendTimedRequest = (url,config)=>{
    return new Promise((resolve, reject)=>{
        fetch(url,{
            ...config,
        }).then(response=>{
            if (response.status >= 200 && response.status < 300) {
                resolve(response);
            } else {
                const error = new Error(`response error.`);
                error.response = response;
                reject(error);
            }
        }).catch(err=>{
            reject(err)
        })
    })    
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22

这是一个最基础发送请求的函数

  1. 下面我们来实现对请求超时的处理

const DEFAULT_TIMEOUT = 1000 * 10; //设置10s的默认超时时间

const sendTimedRequest = (url,config)=>{
    // 超时时间
    const _fetchTimeout = config.timeout||DEFAULT_TIMEOUT
    
    return new Promise((resolve, reject)=>{
        
        // 发送请求的函数
        function sendRquest(){
            
            // 创建一个 AbortController 实例
            const controller = new AbortController();
            const signal = controller.signal; // 用来取消fetch请求
            
            // 超时的定时器
            const timer = setTimeout(()=>{
                controller.abort(); // 取消请求
                sendRquest() // 再次发起请求
            },_fetchTimeout)
            
            fetch(url,{
                ...config.fetchConfig,
                signal
            }).then(response=>{
                
                clearTimeout(timer) // 如果在超时之前返回了就可以清楚定时器了
                
                if (response.status >= 200 && response.status < 300) {
                    resolve(response);
                } else {
                    const error = new Error(`response error.`);
                    error.response = response;
                    reject(error);
                }
            }).catch(err=>{
                if(err.name==='AbortError'){
                    // 处理请求取消的情况
                    console.log('Fetch aborted');
                }else{
                    // 处理其他错误
                    console.error('Fetch error:', err);
                    // 如果请求失败了 则直接关闭超时请求的逻辑
                    clearTimeout(timer)
                    reject(err)
                }
            })
        }
        
        sendRquest()
    })
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53

这里引入了一个判定超时的定时器,定时器的回调里我们会取消掉当前请求,并且重新发起一个请求,重复这个逻辑。需要注意的是取消请求也会被fetch的catch捕获到,所以我们需要在catch里 进行一个判断,如果是捕获到的是取消请求则不改变promise状态,视为整个过程还未完成,因为我们会去重新发起请求。

  1. 下面我们加入重试次数限制

const DEFAULT_TIMEOUT = 1000 * 10; //设置10s的默认超时时间
const DEFAULT_RETRY_TIMES = 1000 * 10; //设置3次的默认重试次数

const sendTimedRequest = (url,config)=>{
    // 超时时间
    const _fetchTimeout = config.timeout||DEFAULT_TIMEOUT
    // 重试次数
    const retryTimes = config.retryTimes||DEFAULT_RETRY_TIMES
    
    return new Promise((resolve, reject)=>{
        
        // 发送请求的函数
        function sendRquest(){
            
            // 创建一个 AbortController 实例
            const controller = new AbortController();
            const signal = controller.signal; // 用来取消fetch请求
            
            // 超时的定时器
            const timer = setTimeout(()=>{
                controller.abort(); // 取消请求
                // 如果重试次数大于0则重新发起请求
                if(retryTimes>0){
                    sendRquest() // 再次发起请求
                    retryTimes--
                }
            },_fetchTimeout)
            
            fetch(url,{
                ...config.fetchConfig,
                signal
            }).then(response=>{
                
                clearTimeout(timer) // 如果在超时之前返回了就可以清楚定时器了
                
                if (response.status >= 200 && response.status < 300) {
                    resolve(response);
                } else {
                    const error = new Error(`response error.`);
                    error.response = response;
                    reject(error);
                }
            }).catch(err=>{
                if(err.name==='AbortError'){
                    // 处理请求取消的情况
                    console.log('Fetch aborted');
                    if(retryTimes===0){
                       reject(err)
                    }
                }else{
                    // 处理其他错误
                    console.error('Fetch error:', err);
                    // 如果请求失败了 则直接关闭超时请求的逻辑
                    clearTimeout(timer)
                    reject(err)
                }
            })
        }
        
        sendRquest()
    })
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63

这里多引入了一个retryTimes字段表示重复请求的次数,在超时的定时器里会去判断当前是否还有可重复执行的次数,没有的话就不会再去发起请求了,并且还是会取消掉当前超时的请求。

这里需要注意的是:在上一步中我们在fetch的catch里捕获到取消请求的错误是不会去改变promise状态的,而这里我们需要再加一个判断,如果捕获到了取消请求的错误,并且retryTimes为0 也就是说请求超时了,并且所有重试次数都耗尽了,那就视为这个请求失败了,可以reject掉这个promise了。

整体的实现过程并不复杂,主要是借助AbortController控制器来取消超时的请求

编辑 (opens new window)
上次更新: 2025/01/17, 17:02:07
小程序实现一个事件中心
超详细的大文件上传实现方案

← 小程序实现一个事件中心 超详细的大文件上传实现方案→

最近更新
01
前端检测更新,自动刷新网页
06-09
02
swiper渲染大量数据的优化方案
06-06
03
仿抖音短视频组件实现方案
02-28
更多文章>
Theme by Vdoing | Copyright © 2023-2025 UzumakiItachi | MIT License
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式