Skip to content

Commit bb588a8

Browse files
committed
fix: fix dataProvider deps analyse algorith
1 parent e59a06a commit bb588a8

2 files changed

Lines changed: 230 additions & 60 deletions

File tree

packages/rcre/src/core/DataProvider/Controller.ts

Lines changed: 33 additions & 59 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import {RootState} from '../../data/reducers';
22
import {ProviderSourceConfig, runTimeType} from '../../types';
33
import {containerActionCreators} from '../Container/action';
4-
import {isEqual, isPlainObject, remove, isEmpty, get, has} from 'lodash';
4+
import {isEqual, isPlainObject, isEmpty, get, has} from 'lodash';
55
import {ContainerProps} from '../Container/Container';
66
import {RunTimeContextCollection} from '../context';
77
import {getRuntimeContext, isPromise} from '../util/util';
@@ -179,7 +179,8 @@ export class DataProvider {
179179
}
180180

181181
public computeRequestGroup(providerList: ProviderSourceConfig[]) {
182-
let taskQueue = [];
182+
let roads: string[] = [];
183+
let taskQueue: string[][] = [];
183184

184185
for (let i = 0; i < providerList.length; i++) {
185186
let provider = providerList[i];
@@ -195,52 +196,50 @@ export class DataProvider {
195196

196197
if (requiredParams.length === 0 && provider.namespace) {
197198
let key = provider.namespace;
198-
199-
let queue = [key];
200199
let paths = {};
201-
this.getRequestTaskQueue(key, this.dependencies[key].beDep, queue, paths);
202-
taskQueue.push(queue);
200+
this.getRequestTaskQueue(key, this.dependencies[key].beDep, key, paths, roads);
203201
}
204202
}
205203

206-
this.taskQueue = taskQueue;
207-
}
204+
roads.sort((prev, next) => {
205+
return next.split('->').length - prev.split('->').length;
206+
});
208207

209-
public getRequestTaskQueue(from: string, deps: string[], taskQueue: string[], paths: { [key: string]: boolean }) {
210-
if (deps.length === 0) {
211-
return;
212-
}
208+
let activeStep = {};
209+
for (let road of roads) {
210+
let step = road.split('->');
213211

214-
deps = deps.slice();
215-
while (deps.length > 0) {
216-
let nextProvider = deps.shift();
212+
for (let i = 0; i < step.length; i ++) {
213+
if (!taskQueue[i]) {
214+
taskQueue[i] = [];
215+
}
217216

218-
if (!nextProvider) {
219-
break;
217+
if (!taskQueue[i].includes(step[i]) && !activeStep[step[i]]) {
218+
activeStep[step[i]] = true;
219+
taskQueue[i].push(step[i]);
220+
}
220221
}
222+
}
221223

222-
let dep = this.dependencies[nextProvider].dep;
223-
224-
remove(dep, d => d === from);
224+
this.taskQueue = taskQueue;
225+
}
225226

226-
if (dep.length > 0) {
227-
continue;
228-
}
227+
public getRequestTaskQueue(key: string, deps: string[], road: string, paths: { [key: string]: boolean }, roads: string[]) {
228+
if (deps.length === 0) {
229+
roads.push(road);
230+
return;
231+
}
229232

230-
if (paths[nextProvider]) {
231-
let f = this.dependencies[nextProvider].beDep.join(',');
233+
for (let nextProvider of deps) {
234+
let existPaths = road.split('->');
232235

233-
console.error(`DataProvider: 发现循环依赖的dataProvider. ${f} <--> ${nextProvider};`);
234-
console.log(this.dependencies);
235-
continue;
236+
if (existPaths.includes(nextProvider)) {
237+
throw new Error(`DataProvider: 发现循环依赖的dataProvider. ${road + '->' + nextProvider}`);
236238
}
237239

238-
if (!taskQueue.includes(nextProvider)) {
239-
taskQueue.push(nextProvider);
240-
paths[nextProvider] = true;
241-
}
240+
let nextRoad = road + '->' + nextProvider;
242241

243-
this.getRequestTaskQueue(nextProvider, this.dependencies[nextProvider].dep, taskQueue, paths);
242+
this.getRequestTaskQueue(nextProvider, this.dependencies[nextProvider].beDep, nextRoad, paths, roads);
244243
}
245244
}
246245

@@ -508,34 +507,9 @@ export class DataProvider {
508507
) {
509508
let runTime = this.getRunTime(model, props, context);
510509
let retCache = {};
511-
512-
let execTask: string[][] = [];
513-
let execIndex = 0;
514-
let maxQueueSize = 1;
510+
let execTask = this.taskQueue;
515511
let isProgress = false;
516512

517-
// 生成请求队列
518-
while (execIndex < maxQueueSize) {
519-
for (let i = 0; i < this.taskQueue.length; i++) {
520-
let task = this.taskQueue[i];
521-
522-
if (task.length > maxQueueSize) {
523-
maxQueueSize = task.length;
524-
}
525-
526-
if (!execTask[execIndex]) {
527-
execTask[execIndex] = [];
528-
}
529-
530-
let exec = task[execIndex];
531-
if (exec) {
532-
execTask[execIndex].push(exec);
533-
}
534-
}
535-
536-
execIndex++;
537-
}
538-
539513
// 并发发送请求
540514
for (let i = 0; i < execTask.length; i++) {
541515
let state: RootState = context.rcre.store.getState();

test/rcre/core/DataProvider.test.tsx

Lines changed: 197 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import {PageConfig, JSONRender} from 'rcre';
44
import {mount} from 'enzyme';
55
import moxios from 'moxios';
66
import {RCRETestUtil, setData} from 'rcre-test-tools';
7+
import {DataProvider} from '../../../packages/rcre/src/core/DataProvider/Controller';
78
import {CoreKind} from '../../../packages/rcre/src/types';
89

910
describe('DataProvider', () => {
@@ -70,6 +71,201 @@ describe('DataProvider', () => {
7071
});
7172
});
7273

74+
it('computer Task Queue', () => {
75+
let dataProvider = new DataProvider([
76+
{
77+
mode: 'ajax',
78+
namespace: 'API1',
79+
config: {
80+
url: '/api1.json'
81+
}
82+
},
83+
{
84+
mode: 'ajax',
85+
namespace: 'API2',
86+
config: {
87+
url: '/api2.json'
88+
},
89+
requiredParams: ['API1', 'API5']
90+
},
91+
{
92+
mode: 'ajax',
93+
namespace: 'API3',
94+
config: {
95+
url: '/api3.json'
96+
},
97+
requiredParams: ['API2', 'API6']
98+
},
99+
{
100+
mode: 'ajax',
101+
namespace: 'API4',
102+
config: {
103+
url: '/api4.json'
104+
},
105+
requiredParams: ['API2', 'API6']
106+
},
107+
{
108+
mode: 'ajax',
109+
namespace: 'API5',
110+
config: {}
111+
},
112+
{
113+
mode: 'ajax',
114+
namespace: 'API6',
115+
config: {},
116+
requiredParams: ['API5']
117+
}
118+
]);
119+
120+
expect(dataProvider.taskQueue).toEqual([['API1', 'API5'], ['API2', 'API6'], ['API3', 'API4']]);
121+
});
122+
123+
it('repeat namespace deps kind 1', () => {
124+
let dataProvider = new DataProvider([
125+
{
126+
mode: 'ajax',
127+
namespace: 'API1',
128+
config: {
129+
url: '/api1.json'
130+
}
131+
},
132+
{
133+
mode: 'ajax',
134+
namespace: 'API2',
135+
config: {
136+
url: '/api2.json'
137+
},
138+
requiredParams: ['API1', 'API5']
139+
},
140+
{
141+
mode: 'ajax',
142+
namespace: 'API3',
143+
config: {
144+
url: '/api3.json'
145+
},
146+
requiredParams: ['API1', 'API2', 'API6']
147+
},
148+
{
149+
mode: 'ajax',
150+
namespace: 'API4',
151+
config: {
152+
url: '/api4.json'
153+
},
154+
requiredParams: ['API2', 'API6']
155+
},
156+
{
157+
mode: 'ajax',
158+
namespace: 'API5',
159+
config: {}
160+
},
161+
{
162+
mode: 'ajax',
163+
namespace: 'API6',
164+
config: {},
165+
requiredParams: ['API5']
166+
}
167+
]);
168+
169+
expect(dataProvider.taskQueue).toEqual([['API1', 'API5'], ['API2', 'API6'], ['API3', 'API4']]);
170+
});
171+
172+
it('repeat namespace deps kind 2', () => {
173+
let dataProvider = new DataProvider([
174+
{
175+
mode: 'ajax',
176+
namespace: 'API1',
177+
config: {
178+
url: '/api1.json'
179+
}
180+
},
181+
{
182+
mode: 'ajax',
183+
namespace: 'API2',
184+
config: {
185+
url: '/api2.json'
186+
},
187+
requiredParams: ['API1', 'API5']
188+
},
189+
{
190+
mode: 'ajax',
191+
namespace: 'API3',
192+
config: {
193+
url: '/api3.json'
194+
},
195+
requiredParams: ['API1', 'API2', 'API6']
196+
},
197+
{
198+
mode: 'ajax',
199+
namespace: 'API4',
200+
config: {
201+
url: '/api4.json'
202+
},
203+
requiredParams: ['API2', 'API6', 'API5', 'API3']
204+
},
205+
{
206+
mode: 'ajax',
207+
namespace: 'API5',
208+
config: {}
209+
},
210+
{
211+
mode: 'ajax',
212+
namespace: 'API6',
213+
config: {},
214+
requiredParams: ['API5']
215+
}
216+
]);
217+
218+
expect(dataProvider.taskQueue).toEqual([['API1', 'API5'], ['API2', 'API6'], ['API3'], ['API4']]);
219+
});
220+
221+
it('circular deps', () => {
222+
expect(() => new DataProvider([
223+
{
224+
mode: 'ajax',
225+
namespace: 'API1',
226+
config: {
227+
url: '/api1.json'
228+
}
229+
},
230+
{
231+
mode: 'ajax',
232+
namespace: 'API2',
233+
config: {
234+
url: '/api2.json'
235+
},
236+
requiredParams: ['API1', 'API5']
237+
},
238+
{
239+
mode: 'ajax',
240+
namespace: 'API3',
241+
config: {
242+
url: '/api3.json'
243+
},
244+
requiredParams: ['API1', 'API2', 'API6']
245+
},
246+
{
247+
mode: 'ajax',
248+
namespace: 'API4',
249+
config: {
250+
url: '/api4.json'
251+
},
252+
requiredParams: ['API2', 'API6', 'API5', 'API3']
253+
},
254+
{
255+
mode: 'ajax',
256+
namespace: 'API5',
257+
config: {},
258+
requiredParams: ['API4']
259+
},
260+
{
261+
mode: 'ajax',
262+
namespace: 'API6',
263+
config: {},
264+
requiredParams: ['API5']
265+
}
266+
])).toThrow('DataProvider: 发现循环依赖的dataProvider. API1->API2->API3->API4->API5->API2');
267+
});
268+
73269
it('requiredParams', () => {
74270
return new Promise((resolve, reject) => {
75271
let config = {
@@ -697,7 +893,7 @@ describe('DataProvider', () => {
697893

698894
let util = new RCRETestUtil(config);
699895
util.setContainer('test');
700-
896+
701897
let username = util.getComponentByName('username');
702898
let strict = util.getComponentByName('strict');
703899
let loose = util.getComponentByName('loose');

0 commit comments

Comments
 (0)