一直以来,PC端管理系统中的所有下拉选项、列表状态等字段展示,一直都是前端直接用一个json配置的,直到有一天,我接到了一个任务:所有枚举都从服务器中读取,后台提供接口。
分析一波: 一个模块很多组件,每个组件中都可能有下拉选项或列表状态显示,所以我必须封着一个全局的方法去调用接口。 然“鹅”,总不能每个要用的地方都调用一次接口吧,所以必须加一个全局对象存储,如果已经有数据了则直接返回,没有的时候才去拉接口。于是,第一版代码出街。大功告成,完结撒花。
const enumerateConfig = {};
export const getEnumerater = function (key, { k, type = 'array' } = {}) {
//返回对应数据类型
const onSuccess = (object) => {
const obj = k ? object[k] : object;
if (type == 'array') {
return Object.values(obj);
}
return obj;
};
const enumData = enumerateConfig[key];
if (enumData) return Promise.resolve(onSuccess(enumData));
return new Promise(async (resolve, reject) => {
request(`xxx/${key}`)
.then((rs) => {
if (rs?.errCode != 0) {
reject();
return;
}
const data = rs.data;
enumerateConfig[key] = data;
resolve(onSuccess(data));
})
.catch(reject);
});
};
正当我美滋滋的写着页面,调用着这个方法时,我发现一个问题:一个页面中好几个组件,且多个组件同时去获取数据,而这是全局对象又没有数据时...好家伙,好几个请求都触发了接口请求。
我的第一反应告诉我,加个正在请求的状态就完事。但往往事情并没有这么简单。设想一下:第一个组件调用时方法进入接口请求,接口正在请求时,第二个第三个组件又调用,这时如果单靠状态判断肯定是不行的,这时根本就没有数据给它返回,那第二个和第三个调用怎么办呢。
突然我灵光一闪:如果第二个和第三个组件调用方法时,我让他先暂停等待呢。等到第一次接口请求完成后数据放入全局对象时再执行第二个和第三个方法的调用,返回结果...。于是,改良版代码出街,完结撒花。
const enumerateConfig = {
pendding: false,
wait: []
};
export const getEnumerater = function (key, { k, type = 'array' } = {}) {
//返回对应数据类型
const onSuccess = (object) => {
const obj = k ? object[k] : object;
if (type == 'array') {
return Object.values(obj);
}
return obj;
};
const enumData = enumerateConfig[key];
if (enumData) return Promise.resolve(onSuccess(enumData));
const resolveWait = () => {
if (enumerateConfig.wait.length == 0) return;
enumerateConfig.wait.forEach(item => {
item(onSuccess(enumerateConfig[key]));
});
enumerateConfig.wait = [];
};
return new Promise(async (resolve, reject) => {
if (enumerateConfig.pendding == key) {
//如果第一次的接口还在请求中,后面的请求都加个进程等待,必须保证第一次接口请求完成从而到达只请求一次接口的目的
enumerateConfig.wait.push(resolve);
return;
}
enumerateConfig.pendding = key;
request(`/xxxx/${key}`)
.then((rs) => {
if (rs?.errCode != 0) {
reject();
return;
}
const data = rs.data;
enumerateConfig[key] = data;
enumerateConfig.pendding = false;
resolveWait();
resolve(onSuccess(data));
})
.catch(() => {
enumerateConfig.pendding = false;
reject();
});
});
};