One of the things front-end development does on a daily basis is make requests. Some pages have complex business logic and more requests to be sent. In order to quickly obtain the results of the interface response and display the page content, it is inevitable to make concurrent requests.
Scenario assumptions
You now have a page that calls many data interfaces when it is initialized, and some of the interfaces have a dependent (sequential) relationship.
Data interface
- RequestA has no front-loaded dependencies
- RequestA1 depends on the response result of the requestA
- RequestA2 depends on the response result of requestA1
- RequestA3 depends on the response result of requestA1
- RequestB without relying on
- RequestB1 depends on the response result of the requestB
- The requestC depends on the response results of the requestA2/requestA3 and requestB1
- RequestD rely on requestC
- RequestD1 rely on requestD
- RequestE without relying on
General concurrent writing
{
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()
}
}
Copy the code
- Some adjustments are needed. RequestB1 no longer depends on the requestB and can request directly. The requestC depends on the response results of the requestA2, requestA3, requestB, and requestB1. The code changes as follows:
{
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()
}
}
Copy the code
- Add interface requestF, which depends on requestD1 and requestE. The code changes are as follows:
{
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)
})
}
}
Copy the code
conclusion
Interface changes, dependency changes, code changes.
thinking
A writing method can not only facilitate the change of interface dependencies, but also be clear about the dependencies between interfaces.
/ / pseudo code
// Dependencies
dependsMap = {
requestA1: [requestA],
[requestA2 | requestA3]: [requestA1],
requestB1: [requestB],
requestC: [requestA2 | requestA3, requestB1],
requestD: [requestC],
requestD1: [requestD]
}
/ / no dependence
init([requestA, requestB, requestE])
Copy the code
Since the dependsMap key is an object, it can be represented using a Map or an array. Arrays are recommended:
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]]
]
Copy the code
RequestB1 no longer depends on requestB; The requestC depends on the response results of the requestA2_or_requestA3, requestB, and requestB1. The code changes are as follows:
depends = [
[requestA1, [requestA]],
[requestA2_or_requestA3, [requestA1]],
[requestC, [requestA2_or_requestA3, requestB, requestB1]],
[requestD, [requestC]],
[requestD1, [requestD]]
]
Copy the code
Add a requestF that depends on requestD1 and requestE.
depends.push([requestF, [requestD1, requestE]])
Copy the code
Code implementation
The above approach is what I call “event dependency.”
The first is to determine the type of event. One is a plain request that returns a Promise. The other is a generic function (or method) that handles the result of the request. (for example, requestA2_or_requestA3)
How to implement
Use state management to change event dependencies to the corresponding states of dependent events.
Take a chestnut
First, register the two events and set the dependencies between them.
const events = [requestA, requestA1];
// Correspond to events in events
// 0 indicates that the event is not executed or is not met
const events_state = [0.0];
const depends = [
[requestA1, [requestA]]
];
Copy the code
The event requestA has no other dependencies and executes immediately. After the execution is complete, the state of the corresponding location is changed. And detect the remaining unexecuted events that meet the dependency state; If the event requestA1 meets the execution status, requestA1 is executed.
code
class EventDepend {
// All registration events
events = [];
// Status of all registration events
eventsState = [];
// Event dependencies
depends = {};
// Register events
register(event) {
const events = Array.isArray(event) ? event : [event];
events.forEach(event= > {
// Filter events that have been registered
if (this.events.includes(event)) return;
this.events.push(event);
this.eventsState.push(0); })}// Set dependencies
depend(event, dependEvents) {
// Events cannot depend on themselves, otherwise they lead to an infinite loop
if (dependEvents.includes(event)) {
throw Error('Events cannot depend on themselves and will cause an endless loop');
}
// Register events
this.register([event, ...dependEvents]);
// Set the dependency state
const index = this.events.indexOf(event);
this.depends[index] = dependEvents.map(
event= > this.dependEvents.indexOf(event)
);
}
// Set multiple dependencies
dependMany(depends) {
depends.forEach(opts= > this.depend(... opts)); }// Execute the event
exec(event) {
const events = Array.isArray(event) ? event : [event];
// Register events
this.register(events);
events.forEach(event= > {
// Execute the event
const result = event();
// There are two types of events: plain functions or return promises
// Common function type. If the return value is false, the event state is not changed
if (result === false) return;
// Change event state & execute events that satisfy state dependence
// If it is of type Promise, change the event state in then
const isPromise = toString.call(result) === '[object Promise]';
const changeState = this.changeState.bind(this, event);
isPromise ? result.then(changeState) : changeState();
});
}
// Change event state & execute dependent events that satisfy the state
changeState(event) {
// Change the event state
const index = this.events.indexOf(event);
this.eventsState[index] = 1;
// Execute dependent events that satisfy the state
const fulfilledKeys = Object.keys(this.depends).filter(
key= > !this.depends[key].find(
index= > this.eventsState[index] === 0)); fulfilledKeys.forEach(key= > {
/ / execution
this.exec(this.events[key]);
// Yes, delete
delete this.depends[key]; }); }}Copy the code
ascension
- Managing state with a single number (bit operations)