Skip to content

Commit 4771a7d

Browse files
authored
Merge pull request #1206 from Cuiyansong/master
refactor(chart): add debounce event action in event chart broker
2 parents fb639cb + efa8882 commit 4771a7d

File tree

3 files changed

+95
-13
lines changed

3 files changed

+95
-13
lines changed

frontend/src/app/components/ChartIFrameContainer/ChartIFrameEventBroker.ts

Lines changed: 50 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,8 @@
1818

1919
import { ChartLifecycle } from 'app/constants';
2020
import { IChart } from 'app/types/Chart';
21+
import { EVENT_ACTION_DELAY_MS } from 'globalConstants';
22+
import debounce from 'lodash/debounce';
2123
import { ValueOf } from 'types';
2224
import { Debugger } from 'utils/debugger';
2325

@@ -33,26 +35,70 @@ type HooksEvent = ValueOf<typeof ChartLifecycle>;
3335

3436
class ChartIFrameEventBroker {
3537
private _listeners: Map<HooksEvent, Function> = new Map();
38+
private _eventCache: Map<
39+
HooksEvent,
40+
{ options?: any; context?: BrokerContext }
41+
> = new Map();
3642
private _chart?: IChart;
43+
private _eventActions = {
44+
[ChartLifecycle.MOUNTED]: this.createImmediatelyAction(
45+
ChartLifecycle.MOUNTED,
46+
),
47+
[ChartLifecycle.UPDATED]: this.createDebounceAction(ChartLifecycle.UPDATED),
48+
[ChartLifecycle.RESIZE]: this.createDebounceAction(ChartLifecycle.RESIZE),
49+
[ChartLifecycle.UNMOUNTED]: this.createImmediatelyAction(
50+
ChartLifecycle.UNMOUNTED,
51+
),
52+
};
3753

38-
register(c: IChart) {
54+
public register(c: IChart) {
3955
this._chart = c;
4056
this.registerListener(c);
4157
}
4258

43-
subscribe(event: HooksEvent, callback?: Function) {
59+
public subscribe(event: HooksEvent, callback?: Function) {
4460
if (!callback || this._listeners.has(event)) {
4561
return;
4662
}
4763
this._listeners.set(event, callback);
4864
}
4965

50-
publish(event: HooksEvent, options, context?: BrokerContext) {
66+
public publish(event: HooksEvent, options, context?: BrokerContext) {
5167
if (!this._listeners.has(event) || !this._listeners.get(event)) {
5268
return;
5369
}
70+
this._eventCache[event] = { options, context };
71+
return this._eventActions[event]?.();
72+
}
5473

55-
this.invokeEvent(event, options, context);
74+
public dispose() {
75+
if (this._listeners && this._listeners.size > 0) {
76+
this._listeners = new Map();
77+
}
78+
if (this._eventCache && this._eventCache.size > 0) {
79+
this._eventCache = new Map();
80+
}
81+
if (this._chart) {
82+
this._chart = undefined;
83+
}
84+
}
85+
86+
private createImmediatelyAction(event: HooksEvent) {
87+
return () => this.actionCreator(event);
88+
}
89+
90+
private createDebounceAction(event: HooksEvent) {
91+
return debounce(() => this.actionCreator(event), EVENT_ACTION_DELAY_MS, {
92+
// NOTE: in order to get a better optimization, we set `EVENT_ACTION_DELAY_MS` milliseconds delay in first time
93+
// see more information with lodash doc https://www.lodashjs.com/docs/lodash.debounce
94+
leading: false,
95+
});
96+
}
97+
98+
private actionCreator(event: HooksEvent) {
99+
const options = this._eventCache[event]?.options;
100+
const context = this._eventCache[event]?.context;
101+
return this.invokeEvent(event, options, context);
56102
}
57103

58104
private invokeEvent(event: HooksEvent, options, context?: BrokerContext) {
@@ -108,12 +154,6 @@ class ChartIFrameEventBroker {
108154
this.subscribe(ChartLifecycle.RESIZE, c?.onResize);
109155
this.subscribe(ChartLifecycle.UNMOUNTED, c?.onUnMount);
110156
}
111-
112-
dispose() {
113-
if (this._listeners && this._listeners.size > 0) {
114-
this._listeners = new Map();
115-
}
116-
}
117157
}
118158

119159
export default ChartIFrameEventBroker;

frontend/src/app/components/ChartIFrameContainer/__tests__/ChartIFrameEventBroker.test.jsx

Lines changed: 44 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -68,13 +68,32 @@ describe('ChartIFrameEventBroker Tests', () => {
6868
expect(chart.onMount).toHaveBeenCalledWith(mockOptions, mockContext);
6969
});
7070

71-
test('should call chart onUpdated lifecycle after mounted', () => {
71+
test('should call chart onUpdated lifecycle after mounted', async () => {
7272
const chart = {
7373
state: 'ready',
7474
onUpdated: jest.fn().mockImplementation(),
7575
};
7676
broker.register(chart);
7777
broker.publish('updated', mockOptions, mockContext);
78+
79+
// NOTE: the milliseconds is from EVENT_ACTION_DELAY_MS
80+
await new Promise(r => setTimeout(r, 300));
81+
expect(chart.onUpdated).toHaveBeenCalledTimes(1);
82+
expect(chart.onUpdated).toHaveBeenCalledWith(mockOptions, mockContext);
83+
});
84+
85+
test('should invoke once onUpdated lifecycle with many update events', async () => {
86+
const chart = {
87+
state: 'ready',
88+
onUpdated: jest.fn().mockImplementation(),
89+
};
90+
broker.register(chart);
91+
broker.publish('updated', mockOptions, mockContext);
92+
broker.publish('updated', mockOptions, mockContext);
93+
broker.publish('updated', mockOptions, mockContext);
94+
95+
// NOTE: the milliseconds is from EVENT_ACTION_DELAY_MS
96+
await new Promise(r => setTimeout(r, 300));
7897
expect(chart.onUpdated).toHaveBeenCalledTimes(1);
7998
expect(chart.onUpdated).toHaveBeenCalledWith(mockOptions, mockContext);
8099
});
@@ -88,13 +107,32 @@ describe('ChartIFrameEventBroker Tests', () => {
88107
expect(chart.onUpdated).toHaveBeenCalledTimes(0);
89108
});
90109

91-
test('should call chart onResize lifecycle after mounted', () => {
110+
test('should call chart onResize lifecycle after mounted', async () => {
92111
const chart = {
93112
state: 'ready',
94113
onResize: jest.fn().mockImplementation(),
95114
};
96115
broker.register(chart);
97116
broker.publish('resize', mockOptions, mockContext);
117+
118+
// NOTE: the milliseconds is from EVENT_ACTION_DELAY_MS
119+
await new Promise(r => setTimeout(r, 300));
120+
expect(chart.onResize).toHaveBeenCalledTimes(1);
121+
expect(chart.onResize).toHaveBeenCalledWith(mockOptions, mockContext);
122+
});
123+
124+
test('should invoke once onResize lifecycle with many resize events', async () => {
125+
const chart = {
126+
state: 'ready',
127+
onResize: jest.fn().mockImplementation(),
128+
};
129+
broker.register(chart);
130+
broker.publish('resize', mockOptions, mockContext);
131+
broker.publish('resize', mockOptions, mockContext);
132+
broker.publish('resize', mockOptions, mockContext);
133+
134+
// NOTE: the milliseconds is from EVENT_ACTION_DELAY_MS
135+
await new Promise(r => setTimeout(r, 300));
98136
expect(chart.onResize).toHaveBeenCalledTimes(1);
99137
expect(chart.onResize).toHaveBeenCalledWith(mockOptions, mockContext);
100138
});
@@ -160,13 +198,16 @@ describe('ChartIFrameEventBroker Tests', () => {
160198
expect(chart.state).toEqual('ready');
161199
});
162200

163-
test('should set state to ready after updated', () => {
201+
test('should set state to ready after updated', async () => {
164202
const chart = {
165203
state: 'ready',
166204
onUpdated: jest.fn().mockImplementation(),
167205
};
168206
broker.register(chart);
169207
broker.publish('updated', mockOptions, mockContext);
208+
209+
// NOTE: the milliseconds is from EVENT_ACTION_DELAY_MS
210+
await new Promise(r => setTimeout(r, 300));
170211
expect(chart.onUpdated).toHaveBeenCalledTimes(1);
171212
expect(chart.state).toEqual('ready');
172213
});

frontend/src/globalConstants.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ import { FONT_FAMILY } from 'styles/StyleConstants';
2121
export const DATARTSEPERATOR = '@datart@';
2222
export const CHARTCONFIG_FIELD_PLACEHOLDER_UID = '@placeholder@';
2323
export const DATART_TRANSLATE_HOLDER = '@global@';
24+
export const EVENT_ACTION_DELAY_MS = 200;
2425

2526
export enum StorageKeys {
2627
AuthorizationToken = 'AUTHORIZATION_TOKEN',

0 commit comments

Comments
 (0)