本文介绍如何使用Promise实现简单的Ajax请求缓存,避免同一接口被重复发起。通过缓存请求对象而非结果值,确保多个组件共享最新Token,并在失败时自动刷新。方案涵盖七牛上传示例及配置加载场景,提供重试、错误处理等优化思路,无需引入额外缓存层即可提升前端性能与数据一致性。
业务场景与需求分析
业务场景
在许多实际业务中,我们需要对某些接口进行简单的请求缓存,确保特定请求仅被发起一次。典型的例子包括获取上传所需的Token或读取系统配置接口等。
利用Promise机制,我们可以直接对这些接口实现简单的缓存控制,并支持动态更新,从而避免引入额外的第三方缓存层。
基于Promise的Ajax缓存实现
示范代码
以七牛云存储上传为例,通常我们会将上传功能封装为一个独立的Upload组件。外部调用时只需触发组件,而Token的获取逻辑则封装在组件内部处理。
//Upload.vue
let fetchToken = null;
export default {
data() {
return {
token: ''
};
},
methods: {
async upload() {
try {
// ...
}
catch(err) {
alert(err.message);
this.refreshToken();
}
},
refreshToken() {
fetchToken = null;
this.fetchToken();
},
fetchToken() {
if (!fetchToken) {
fetchToken = request.get('/api/qiniu/token');
}
try {
this.token = await fetchToken;
}
catch(err) {
console.error(err);
}
}
},
created() {
this.fetchToken();
}
};
上述代码展示了缓存上传Token的基本实现,并在上传失败时具备刷新Token的机制。
缓存请求与缓存值的对比
与直接缓存Token数值相比,缓存整个请求对象有哪些优势?
// 缓存值的代码
export default {
methods: {
fetchToken() {
if (!fetchToken) {
fetchToken = await request.get('/api/qiniu/token');
}
try {
this.token = fetchToken;
}
catch(err) {
console.error(err);
}
}
}
}
考虑一个常见的Upload组件应用场景:单个页面中同时存在多个该组件实例。
<template>
<div class="upload1"><upload /></div>
<div class="upload2"><upload /></div>
</template>
若采用直接缓存值的方法,如前文代码所示,页面加载时会触发两次获取Token接口的请求。
组件Token同步与优化策略
继续完善Upload组件逻辑
//Upload.vue
let fetchToken = null;
export default {
methods: {
async upload() {
try {
this.fetchToken();
const token = await fetchToken;
// ...
} catch (err) {
alert(err.message);
this.refreshToken();
}
},
refreshToken() {
fetchToken = null;
this.fetchToken();
},
fetchToken() {
if (!fetchToken) {
fetchToken = request.get('/api/qiniu/token');
}
}
},
created() {
this.fetchToken();
}
};
为避免多个Upload组件间Token不同步的问题,不再通过实例属性this.token保存Token,而是每次均等待fetchToken解析完成,确保获取到的Token始终是最新的。
当然,该方案仍有优化空间,例如增加失败重试机制、仅在401错误时刷新Token、设置错误时间戳或定时刷新等,但核心思路已在上述代码中体现。
配置加载场景应用
此外,再介绍一个经典应用场景
const fetchConfig = (() => {
let configRequest = null;
return () => {
if (!configRequest) {
configRequest = Promise.all([services.customer.config1, services.customer.config2])
.then(([data1, data2]) => {
return { data1, data2 };
})
.catch(err => {
configRequest = null;
return Promise.reject(err);
});
}
return configRequest;
};
})();
export default {
async beforeRouteEnter(to, from, next) {
try {
// 配置信息仅需要成功请求一次
const [data, config] = await Promise.all([services.customer.getInfo(), fetchConfig()]);
next(vm => {
vm.data = data;
vm.config = config;
vm.init();
};
} catch (err) {
next(err);
}
}
};
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持本站。