前端:如何处理AJAX请求的重复使用

开发 前端
在开发前端时,我们经常使用AJAX来初始化数据并动态渲染在页面上,但是在遇到一连串的相同数据都要进行请求时,就有可能对同一个API 发出并发请求,然而,因为这些请求是同时发出,因此响应也非常可能是相同的,这样讲可能不够清楚,直接写一个简易的范例来解释这个情况。

[[378036]]

 在开发前端时,我们经常使用AJAX来初始化数据并动态渲染在页面上,但是在遇到一连串的相同数据都要进行请求时,就有可能对同一个API 发出并发请求,然而,因为这些请求是同时发出,因此响应也非常可能是相同的,这样讲可能不够清楚,直接写一个简易的范例来解释这个情况。

实际范例

首先我们先撰写一个API:

 

https://localhost:3000/api/v1/users/:uuid 
  • 1.

这个API的回传值如下:

 


    "name":"Username{uuid}"
    "uuid":"{uuid}" 

  • 1.
  • 2.
  • 3.
  • 4.

随后开一个Vue的demo,并且先通过Axios写一个请求的函数:

 

// fetch-user.js 
 
const axios = require('axios'); 
 
module.exports = (uuid) => { 
    let uri = `http://localhost:3000/users/${uuid}`; 
    return new Promise(resolve => { 
        axios.get(uri).then(resolve); 
    }) 
}; 
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.

然后我们在Vue例子中新增一个User Component(User.vue)来负责渲染并请求接口:

<template> 
    <div v-if="init"
        <ul> 
            <li>{{user.name}}</li> 
            <li>{{user.uuid}}</li> 
        </ul> 
    </div> 
</template> 
 
<script> 
    const fetchUser = require('../lib/fetch-user'); 
    export default { 
        name'User'
        data: function() { 
            return { 
                init: false
                usernull 
            } 
        }, 
        props: { 
            uuid: String 
        }, 
        async mounted() { 
            const response = await fetchUser(this.uuid); 
            this.init = true
            this.user = response.data; 
        } 
    } 
</script> 
  • 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.

最后将用户组件放入App.vue中:

<template> 
    <div id="app"
        <user uuid="user-uuid"></user
        <user uuid="user-uuid"></user
        <user uuid="user-uuid"></user
        <user uuid="user-uuid"></user
        <user uuid="user-uuid"></user
        <user uuid="user-uuid"></user
        <user uuid="user-uuid"></user
        <user uuid="user-uuid"></user
        <user uuid="user-uuid"></user
        <user uuid="user-uuid"></user
        <user uuid="user-uuid"></user
        <user uuid="user-uuid"></user
        <user uuid="user-uuid"></user
        <user uuid="user-uuid"></user
        <user uuid="user-uuid"></user
    </div> 
</template> 
 
<script> 
import User from './components/User'
 
export default { 
    name'App'
    components: { 
        User 
    } 

</script> 
  • 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.

接着我们看一下显示结果:

 

 

 

 

这样就正确显示了,然而这里有一个问题非常值得注意:

 

 

 

 

我们打开开发者模式就会发现,每个组件向该API发出了请求,因此就产生了10次的并发请求,但是在这种情况下,实际上我们仅需要让一个请求出去,另外9个元件等待这个请求的响应然后重新使用即可。

改进的方法

接下来将讲解要如何实现关于在同一个组件之间唯一指定API请求一次并分配请求,我们会用到这个元件EventTarget,这个元件有点类似Node.js中的EventEmitter,主要就是用于接收事件。

随后我们改写fetchUser()函数:

 

const axios = require('axios'); 
 
/** 
 * 这个 class 是用于存储 Response Data 的 Event 衍生类 
 */ 
class FetchCompleteEvent extends Event { 
    constructor(type, data) { 
        super(type); 
        this.data = data; 
    } 

 
// 用于请求成功时使用的事件监听器 
const eventEmitter = new EventTarget(); 
 
// 用于请求失敗时使用的事件监听器 
const errorEmitter = new EventTarget(); 
 
/** 
 * 用于存储 URI 以及是否当前正在请求的状态,如: 
 * http://localhost:8000/users/foo => true 代表已经发出请求,正在等待 Response 
 * http://localhost:8000/users/bar => false 代表当前没有请求在路上 
 */ 
 
const requestingList = new Map(); 
 
module.exports = (uuid) => { 
 
    let uri = `http://localhost:3000/users/${uuid}`; 
 
    return new Promise((resolve, reject) => { 
 
        // 如果没有记录,或者尚未处于请求状态 
        if (!requestingList.has(uri) || !requestingList.get(uri)) { 
 
            // 进入之后立即将请求状态设为 true 
            requestingList.set(uri, true); 
 
            // 请求 URI 
            axios.get(uri).then(response => { 
 
                // 完成请求之后将请求状态设为 false 
                requestingList.set(uri, false); 
 
                // 发出一个事件通知來告诉 callback 请求完成了 
                eventEmitter.dispatchEvent(new FetchCompleteEvent(uri, response)); 
                resolve(response); 
 
            }).catch((e) => { 
 
                // 请求失败也算是请求完成,将请求状态设为 false 
                requestingList.set(uri, false); 
 
                // 发出一个事件通知來告诉 callback 请求失败了 
                errorEmitter.dispatchEvent(new FetchCompleteEvent(uri, e)); 
                reject(e); 
 
            }) 
        } 
         // 当目前指定的 URL 处于请求状态,则不做任何事情 
        else { 
 
            // 向成功的事件监听器注册,当完成之后 resolve() 
            eventEmitter.addEventListener(uri, (event) => { 
                resolve(event.data); 
            }); 
 
            // 失败之后 reject() 
            errorEmitter.addEventListener(uri, (event) => { 
                reject(event.data); 
            }) 
        } 
    }); 
}; 
  • 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.
  • 64.
  • 65.
  • 66.
  • 67.
  • 68.
  • 69.
  • 70.
  • 71.
  • 72.
  • 73.
  • 74.

接着我们重新运行前端应用程序并查看结果:

 

 

 

 

结果与一开始一模一样,而是当时我们打开开发者模式就会发现:

 

 

 

 

请求已经被减少到剩下一个了,这是因为所有的元件都重复使用了一个同一个响应。通过这种方法将可以大大减少服务器的负载以及前端的运行时间。

总结

并非每一种情况下都可以使用这种方式来请求资源,如:每次请求资源都一定会发送不一样的API就不能使用这种方式进行API调用,但是像是上述范例中的用户资料,电商网站中的商品资料或文章等,类似能够确保在极短时间之内资源都是相同的API就可以使用这种方式来进行操作。

扩展阅读

https://dev.to/floatflower/ajax-414j

参考资料

1.https://developer.mozilla.org/zh-TW/docs/Web/API/EventTarget

 

责任编辑:姜华 来源: 前端简报
相关推荐

2009-07-15 18:07:47

JDBC代码

2021-06-17 09:32:39

重复请求并发请求Java

2023-10-04 07:35:03

2021-01-26 13:40:44

mysql数据库

2024-10-16 17:04:13

2025-01-09 10:20:53

2023-09-19 22:41:30

控制器HTTP

2011-09-02 11:06:28

Oracle服务器进程为事务建立回滚段放入dirty lis

2012-08-13 10:23:33

IBMdW

2021-01-18 05:13:04

TomcatHttp

2020-12-03 07:43:03

JS Ajax JavaScript

2024-10-23 08:00:00

2024-12-19 08:00:00

2023-08-10 10:58:24

2012-12-12 09:49:41

2017-03-13 13:21:34

Git处理大仓库

2020-12-29 09:11:33

LinuxLinux内核

2011-12-15 12:32:19

JavaNIO

2018-11-21 12:27:21

JavaScript 货币值区域

2024-08-23 09:00:18

开发跨域请求
点赞
收藏

51CTO技术栈公众号