一、用户uid为空的问题
背景:因为该应用要求必须有一个uid,如果用户没有注册或者登录会默认为用户注册一个游客id,并且会将uid等信息带在每个请求的url上。
遇到的问题:由于游客id的获取(由服务端生成)和其他获取数据的请求是差不多同一时间发出的,那么此时获取数据的接口他的url是没有带上uid的,假如这个接口需要依赖uid信息那么获取数据就会失败。
如何发现的:某个页面接入了个性化推荐,因此uid必传,在测试中发现清空了浏览器缓存数据以后首次访问页面个性化推荐无数据。
我的解决方案:该项目有一个统一的request.js文件,它是一个单例模式,所有请求最终都会通过这个js去发起,因此我在这里去做了请求拦截,当某一个请求调用到来时,先不去发起网络请求,而是去校验是否有游客id,如果有则正常发起请求,如果没有则缓存当前请求到队列,另外判断当前是否在获取游客id,没有则发起一个获取游客id的请求,等到游客id请求成功时,统一释放队列中缓存的请求并抛出请求返回的数据。伪代码:
function Request(url, option) {
option = getOption(option, ctx);
// 有uid走正常流程
if (option.params.uid) {
// 发起正常的网络请求请求
}
// 获取游客id
getVisitorID();
// 缓存请求队列,这里我们返回一个Promise对象,方便后期游客队列执行时准确获取数据
return cacheQueue(url, option, (url, option, resolve, reject) => {
// 此处返回一发送请求的方法,在请求成功的then方法里面使用resolve将结果返回
return request(url, option).then(function (res) {
resolve(res);
})
});
}
缓存队列实现思路:
function cacheQueue(url, option, cb = () => { }) {
// 通过返回一个Promise对象使函数调用房持有,等待队列函数执行时通过resolve和reject可以获得准确的返回值
return new Promise((resolve, reject) => {
// 将游客id生成前发起的请求加入缓存队列
Request.requestList.push((res) => {
try {
// 若该请求是获取游客id的请求,直接回传游客数据,用于防止其他请求比游客id的请求先一步执行,此时我们不必再次获取游客id,而是将当前的id通过resolve丢回去即可
if (url.indexOf(getVisitorIDUrl) > -1) {
resolve(res);
return;
}
option.params.uid = res.data.uid;
return cb(url, option, ctx, resolve, reject)
} catch (error) {
reject(error);
}
});
});
}