前端开发日常要做的一件事就是发请求。 有些页面业务逻辑复杂,要发送的请求较多;为了能够快速获取接口响应结果,展示页面内容,这时不免就需要进行并发请求。
场景假设
现在有个页面在初始化时,要调用很多数据接口,并且有些接口之间有依赖(先后)的关系。
数据接口
- requestA 没有前置依赖
- requestA1 依赖requestA的响应结果
- requestA2 依赖requestA1的响应结果
- requestA3 依赖requestA1的响应结果
- requestB 无依赖
- requestB1 依赖requestB的响应结果
- requestC 依赖requestA2/requestA3和requestB1的响应结果
- requestD 依赖requestC
- requestD1 依赖requestD
- requestE 无依赖
一般并发写法
{
mounted() {
const p1 = new Promise(resolve => {
requestA().then(ARes => {
requestA1(ARes).then(A1Res => {
if (A1Res.status === 1) requestA2(A1Res).then(resolve)
else requestA3(A1Res).then(resolve)
})
})
})
const p2 = new Promise(resolve => {
requestB().then(BRes => {
requestB1(BRes).then(resolve)
})
})
Promise.all([p1, p2]).then(items => {
requestC(items).then(CRes => {
requestD(CRes).then(DRes => {
requestD1(DRes)
})
})
})
requestE()
}
}
- 现在需要进行一些调整。requestB1不再依赖requestB,可以直接请求;requestC依赖requestA2/requestA3、requestB、requestB1的响应结果。代码进行如下改动:
{
mounted() {
const p1 = new Promise(resolve => {
requestA().then(ARes => {
requestA1(ARes).then(A1Res => {
if (A1Res.status === 1) requestA2(A1Res).then(resolve)
else requestA3(A1Res).then(resolve)
})
})
})
Promise.all([p1, requestB, requestB1]).then(items => {
requestC(items).then(CRes => {
requestD(CRes).then(DRes => {
requestD1(DRes)
})
})
})
requestE()
}
}
- 新增接口requestF,依赖requestD1、requestE。代码改动如下:
{
mounted() {
const p1 = new Promise(resolve => {
requestA().then(ARes => {
requestA1(ARes).then(A1Res => {
if (A1Res.status === 1) requestA2(A1Res).then(resolve)
else requestA3(A1Res).then(resolve)
})
})
})
const p2 = new Promise(resolve => {
requestB().then(BRes => {
requestB1(BRes).then(resolve)
})
})
const p3 = new Promise(resolve => {
Promise.all([p1, p2]).then(items => {
requestC(items).then(CRes => {
requestD(CRes).then(DRes => {
requestD1(DRes).then(resolve)
})
})
})
})
Promise.all([p3, requestE]).then(items => {
requestE(items)
})
}
}
总结
接口变化、依赖关系变化,代码改动较大。
思考
采取一种写法,既可以方便应对接口间的依赖关系变化,又能够一目了然接口相互间的依赖关系。
// 伪代码
// 依赖关系
dependsMap = {
requestA1: [requestA],
[requestA2 | requestA3]: [requestA1],
requestB1: [requestB],
requestC: [requestA2 | requestA3, requestB1],
requestD: [requestC],
requestD1: [requestD]
}
// 无依赖
init([requestA, requestB, requestE])
由于dependsMap的key是一个对象,可以使用Map或数组来表示。推荐使用数组表示:
requestA2_or_reuqestA3(A1Res) {
if (A1Res.status === 1) return reuqestA2()
return requestA3()
}
depends = [
[requestA1, [requestA]],
[requestA2_or_requestA3, [requestA1]],
[requestB1, [requestB]],
[requestC, [requestA2_or_requestA3, requestB1]],
[requestD, [requestC]],
[requestD1, [requestD]]
]
requestB1不再依赖requestB;requestC依赖requestA2_or_requestA3、requestB、requestB1的响应结果。代码改动如下:
depends = [
[requestA1, [requestA]],
[requestA2_or_requestA3, [requestA1]],
[requestC, [requestA2_or_requestA3, requestB, requestB1]],
[requestD, [requestC]],
[requestD1, [requestD]]
]
新增requestF, 依赖requestD1、requestE。
depends.push([requestF, [requestD1, requestE]])
代码实现
上面的实现方式,我将之称之为“事件依赖”。
首先要确定事件的类型,一种是普通的请求,返回值是一个Promise;另一种就是普通的函数(或方法),用来对请求结果进行处理。(如,requestA2_or_requestA3)
如何实现
使用状态管理的方式,将事件依赖变为依赖事件对应的状态。
举个栗子
如上,requestA、requestA1两个事件,首先注册这两个事件并设置它们间的依赖关系。
const events = [requestA, requestA1];
// 分别对应events中的事件
// 0表示事件未执行或不满足
const events_state = [0, 0];
const depends = [
[requestA1, [requestA]]
];
事件requestA没有其它依赖,会立即执行,执行完成后,会修改对应位置的state;并且检测剩余未执行事件中有满足依赖状态的;此时检测到事件requestA1满足执行状态,则执行requestA1。
代码
class EventDepend {
// 所有注册事件
events = [];
// 所有注册事件状态
eventsState = [];
// 事件依赖关系
depends = {};
// 注册事件
register(event) {
const events = Array.isArray(event) ? event : [event];
events.forEach(event => {
// 过滤已被注册的事件
if (this.events.includes(event)) return;
this.events.push(event);
this.eventsState.push(0);
})
}
// 设置依赖关系
depend(event, dependEvents) {
// 事件不能依赖自身,否则导致死循环
if (dependEvents.includes(event)) {
throw Error('事件不能依赖自身,将导致死循环');
}
// 注册事件
this.register([event, ...dependEvents]);
// 设置依赖状态关系
const index = this.events.indexOf(event);
this.depends[index] = dependEvents.map(
event => this.dependEvents.indexOf(event)
);
}
// 设置多个依赖关系
dependMany(depends) {
depends.forEach(opts => this.depend(...opts));
}
// 执行事件
exec(event) {
const events = Array.isArray(event) ? event : [event];
// 注册事件
this.register(events);
events.forEach(event => {
// 执行事件
const result = event();
// 事件有两种类型:普通函数或返回Promise
// 普通函数类型,若返回值为false,表示不改变事件状态
if (result === false) return;
// 改变事件状态 & 执行满足状态依赖的事件
// 如果是Promise类型,则在then中改变事件状态
const isPromise = toString.call(result) === '[object Promise]';
const changeState = this.changeState.bind(this, event);
isPromise ? result.then(changeState) : changeState();
});
}
// 改变事件状态 & 执行满足状态的依赖事件
changeState(event) {
// 改变事件状态
const index = this.events.indexOf(event);
this.eventsState[index] = 1;
// 执行满足状态的依赖事件
const fulfilledKeys = Object.keys(this.depends).filter(
key => !this.depends[key].find(
index => this.eventsState[index] === 0
)
);
fulfilledKeys.forEach(key => {
// 执行
this.exec(this.events[key]);
// 已执行,删除
delete this.depends[key];
});
}
}
提升
- 使用单个数字管理状态(位运算)