@khanacademy/wonder-blocks-timing 1.2.3 → 2.0.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/es/index.js +151 -48
- package/dist/index.js +309 -159
- package/docs.md +28 -18
- package/package.json +4 -4
- package/src/components/__tests__/action-scheduler-provider.test.js +3 -3
- package/src/components/action-scheduler-provider.js +2 -2
- package/src/components/with-action-scheduler.js +8 -4
- package/src/hooks/__tests__/use-timeout.test.js +336 -0
- package/src/hooks/use-timeout.js +70 -0
- package/src/hooks/use-timeout.stories.mdx +152 -0
- package/src/index.js +2 -0
- package/src/util/__tests__/action-scheduler.test.js +103 -88
- package/src/util/__tests__/animation-frame.test.js +29 -20
- package/src/util/__tests__/interval.test.js +51 -25
- package/src/util/__tests__/timeout.test.js +35 -20
- package/src/util/action-scheduler.js +41 -16
- package/src/util/animation-frame.js +26 -13
- package/src/util/interval.js +19 -13
- package/src/util/policies.js +10 -0
- package/src/util/timeout.js +23 -13
- package/src/util/types.js +26 -26
|
@@ -1,11 +1,16 @@
|
|
|
1
1
|
// @flow
|
|
2
2
|
import Interval from "../interval.js";
|
|
3
|
+
import {SchedulePolicy, ClearPolicy} from "../policies.js";
|
|
3
4
|
|
|
4
5
|
describe("Interval", () => {
|
|
5
6
|
beforeEach(() => {
|
|
6
7
|
jest.useFakeTimers();
|
|
7
8
|
});
|
|
8
9
|
|
|
10
|
+
afterEach(() => {
|
|
11
|
+
jest.restoreAllMocks();
|
|
12
|
+
});
|
|
13
|
+
|
|
9
14
|
describe("constructor", () => {
|
|
10
15
|
it("creates instance", () => {
|
|
11
16
|
// Arrange
|
|
@@ -41,22 +46,27 @@ describe("Interval", () => {
|
|
|
41
46
|
);
|
|
42
47
|
});
|
|
43
48
|
|
|
44
|
-
it("sets an interval when
|
|
49
|
+
it("sets an interval when schedule policy is SchedulePolicy.Immediately", () => {
|
|
45
50
|
// Arrange
|
|
51
|
+
const intervalSpy = jest.spyOn(global, "setInterval");
|
|
46
52
|
|
|
47
53
|
// Act
|
|
48
54
|
// eslint-disable-next-line no-new
|
|
49
|
-
new Interval(() => {}, 1000,
|
|
55
|
+
new Interval(() => {}, 1000, SchedulePolicy.Immediately);
|
|
50
56
|
|
|
51
57
|
// Assert
|
|
52
|
-
expect(
|
|
58
|
+
expect(intervalSpy).toHaveBeenCalledTimes(1);
|
|
53
59
|
});
|
|
54
60
|
});
|
|
55
61
|
|
|
56
62
|
describe("isSet", () => {
|
|
57
63
|
it("is false when the interval has not been set", () => {
|
|
58
64
|
// Arrange
|
|
59
|
-
const interval = new Interval(
|
|
65
|
+
const interval = new Interval(
|
|
66
|
+
() => {},
|
|
67
|
+
1000,
|
|
68
|
+
SchedulePolicy.OnDemand,
|
|
69
|
+
);
|
|
60
70
|
|
|
61
71
|
// Act
|
|
62
72
|
const result = interval.isSet;
|
|
@@ -94,13 +104,18 @@ describe("Interval", () => {
|
|
|
94
104
|
describe("#set", () => {
|
|
95
105
|
it("should call setInterval", () => {
|
|
96
106
|
// Arrange
|
|
97
|
-
const
|
|
107
|
+
const intervalSpy = jest.spyOn(global, "setInterval");
|
|
108
|
+
const interval = new Interval(
|
|
109
|
+
() => {},
|
|
110
|
+
500,
|
|
111
|
+
SchedulePolicy.OnDemand,
|
|
112
|
+
);
|
|
98
113
|
|
|
99
114
|
// Act
|
|
100
115
|
interval.set();
|
|
101
116
|
|
|
102
117
|
// Assert
|
|
103
|
-
expect(
|
|
118
|
+
expect(intervalSpy).toHaveBeenNthCalledWith(
|
|
104
119
|
1,
|
|
105
120
|
expect.any(Function),
|
|
106
121
|
500,
|
|
@@ -109,12 +124,15 @@ describe("Interval", () => {
|
|
|
109
124
|
|
|
110
125
|
it("should invoke setInterval to call the given action", () => {
|
|
111
126
|
// Arrange
|
|
127
|
+
const intervalSpy = jest.spyOn(global, "setInterval");
|
|
112
128
|
const action = jest.fn();
|
|
113
|
-
const interval = new Interval(
|
|
129
|
+
const interval = new Interval(
|
|
130
|
+
() => action(),
|
|
131
|
+
500,
|
|
132
|
+
SchedulePolicy.OnDemand,
|
|
133
|
+
);
|
|
114
134
|
interval.set();
|
|
115
|
-
|
|
116
|
-
// $FlowFixMe[prop-missing]
|
|
117
|
-
const scheduledAction = setInterval.mock.calls[0][0];
|
|
135
|
+
const scheduledAction = intervalSpy.mock.calls[0][0];
|
|
118
136
|
|
|
119
137
|
// Act
|
|
120
138
|
scheduledAction();
|
|
@@ -126,12 +144,16 @@ describe("Interval", () => {
|
|
|
126
144
|
it("should clear the active interval", () => {
|
|
127
145
|
// Arrange
|
|
128
146
|
const action = jest.fn();
|
|
129
|
-
const interval = new Interval(
|
|
147
|
+
const interval = new Interval(
|
|
148
|
+
() => action(),
|
|
149
|
+
500,
|
|
150
|
+
SchedulePolicy.OnDemand,
|
|
151
|
+
);
|
|
130
152
|
interval.set();
|
|
131
153
|
|
|
132
154
|
// Act
|
|
133
155
|
interval.set();
|
|
134
|
-
jest.
|
|
156
|
+
jest.advanceTimersByTime(501);
|
|
135
157
|
|
|
136
158
|
// Assert
|
|
137
159
|
expect(action).toHaveBeenCalledTimes(1);
|
|
@@ -144,7 +166,7 @@ describe("Interval", () => {
|
|
|
144
166
|
interval.set();
|
|
145
167
|
|
|
146
168
|
// Act
|
|
147
|
-
jest.
|
|
169
|
+
jest.advanceTimersByTime(1501);
|
|
148
170
|
|
|
149
171
|
// Assert
|
|
150
172
|
expect(action).toHaveBeenCalledTimes(3);
|
|
@@ -160,47 +182,51 @@ describe("Interval", () => {
|
|
|
160
182
|
|
|
161
183
|
// Act
|
|
162
184
|
interval.clear();
|
|
163
|
-
jest.
|
|
185
|
+
jest.advanceTimersByTime(501);
|
|
164
186
|
|
|
165
187
|
// Assert
|
|
166
188
|
expect(action).not.toHaveBeenCalled();
|
|
167
189
|
});
|
|
168
190
|
|
|
169
|
-
it("should invoke the action if
|
|
191
|
+
it("should invoke the action if clear policy is ClearPolicy.Resolve", () => {
|
|
170
192
|
// Arrange
|
|
171
193
|
const action = jest.fn();
|
|
172
194
|
const interval = new Interval(action, 500);
|
|
173
195
|
interval.set();
|
|
174
196
|
|
|
175
197
|
// Act
|
|
176
|
-
interval.clear(
|
|
177
|
-
jest.
|
|
198
|
+
interval.clear(ClearPolicy.Resolve);
|
|
199
|
+
jest.advanceTimersByTime(501);
|
|
178
200
|
|
|
179
201
|
// Assert
|
|
180
202
|
expect(action).toHaveBeenCalledTimes(1);
|
|
181
203
|
});
|
|
182
204
|
|
|
183
|
-
it("should not invoke the action if
|
|
205
|
+
it("should not invoke the action if clear policy is ClearPolicy.Cancel", () => {
|
|
184
206
|
// Arrange
|
|
185
207
|
const action = jest.fn();
|
|
186
|
-
const interval = new Interval(
|
|
208
|
+
const interval = new Interval(
|
|
209
|
+
action,
|
|
210
|
+
500,
|
|
211
|
+
SchedulePolicy.Immediately,
|
|
212
|
+
);
|
|
187
213
|
|
|
188
214
|
// Act
|
|
189
|
-
interval.clear(
|
|
190
|
-
jest.
|
|
215
|
+
interval.clear(ClearPolicy.Cancel);
|
|
216
|
+
jest.advanceTimersByTime(501);
|
|
191
217
|
|
|
192
218
|
// Assert
|
|
193
219
|
expect(action).not.toHaveBeenCalled();
|
|
194
220
|
});
|
|
195
221
|
|
|
196
|
-
it("should not invoke the action if interval is inactive", () => {
|
|
222
|
+
it("should not invoke the action if interval is inactive and clear policy is ClearPolicy.Resolve", () => {
|
|
197
223
|
// Arrange
|
|
198
224
|
const action = jest.fn();
|
|
199
|
-
const interval = new Interval(action, 500,
|
|
225
|
+
const interval = new Interval(action, 500, SchedulePolicy.OnDemand);
|
|
200
226
|
|
|
201
227
|
// Act
|
|
202
|
-
interval.clear(
|
|
203
|
-
jest.
|
|
228
|
+
interval.clear(ClearPolicy.Resolve);
|
|
229
|
+
jest.advanceTimersByTime(501);
|
|
204
230
|
|
|
205
231
|
// Assert
|
|
206
232
|
expect(action).not.toHaveBeenCalled();
|
|
@@ -1,11 +1,16 @@
|
|
|
1
1
|
// @flow
|
|
2
2
|
import Timeout from "../timeout.js";
|
|
3
|
+
import {SchedulePolicy, ClearPolicy} from "../policies.js";
|
|
3
4
|
|
|
4
5
|
describe("Timeout", () => {
|
|
5
6
|
beforeEach(() => {
|
|
6
7
|
jest.useFakeTimers();
|
|
7
8
|
});
|
|
8
9
|
|
|
10
|
+
afterEach(() => {
|
|
11
|
+
jest.restoreAllMocks();
|
|
12
|
+
});
|
|
13
|
+
|
|
9
14
|
describe("constructor", () => {
|
|
10
15
|
it("creates instance", () => {
|
|
11
16
|
// Arrange
|
|
@@ -41,22 +46,23 @@ describe("Timeout", () => {
|
|
|
41
46
|
);
|
|
42
47
|
});
|
|
43
48
|
|
|
44
|
-
it("sets a timeout when
|
|
49
|
+
it("sets a timeout when schedule policy is SchedulePolicy.Immediately", () => {
|
|
45
50
|
// Arrange
|
|
51
|
+
const timeoutSpy = jest.spyOn(global, "setTimeout");
|
|
46
52
|
|
|
47
53
|
// Act
|
|
48
54
|
// eslint-disable-next-line no-new
|
|
49
|
-
new Timeout(() => {}, 0,
|
|
55
|
+
new Timeout(() => {}, 0, SchedulePolicy.Immediately);
|
|
50
56
|
|
|
51
57
|
// Assert
|
|
52
|
-
expect(
|
|
58
|
+
expect(timeoutSpy).toHaveBeenCalledTimes(1);
|
|
53
59
|
});
|
|
54
60
|
});
|
|
55
61
|
|
|
56
62
|
describe("isSet", () => {
|
|
57
63
|
it("is false when the timeout has not been set", () => {
|
|
58
64
|
// Arrange
|
|
59
|
-
const timeout = new Timeout(() => {}, 0,
|
|
65
|
+
const timeout = new Timeout(() => {}, 0, SchedulePolicy.OnDemand);
|
|
60
66
|
|
|
61
67
|
// Act
|
|
62
68
|
const result = timeout.isSet;
|
|
@@ -94,13 +100,14 @@ describe("Timeout", () => {
|
|
|
94
100
|
describe("#set", () => {
|
|
95
101
|
it("should call setTimeout", () => {
|
|
96
102
|
// Arrange
|
|
97
|
-
const
|
|
103
|
+
const timeoutSpy = jest.spyOn(global, "setTimeout");
|
|
104
|
+
const timeout = new Timeout(() => {}, 500, SchedulePolicy.OnDemand);
|
|
98
105
|
|
|
99
106
|
// Act
|
|
100
107
|
timeout.set();
|
|
101
108
|
|
|
102
109
|
// Assert
|
|
103
|
-
expect(
|
|
110
|
+
expect(timeoutSpy).toHaveBeenNthCalledWith(
|
|
104
111
|
1,
|
|
105
112
|
expect.any(Function),
|
|
106
113
|
500,
|
|
@@ -109,12 +116,15 @@ describe("Timeout", () => {
|
|
|
109
116
|
|
|
110
117
|
it("should invoke setTimeout to call the given action", () => {
|
|
111
118
|
// Arrange
|
|
119
|
+
const timeoutSpy = jest.spyOn(global, "setTimeout");
|
|
112
120
|
const action = jest.fn();
|
|
113
|
-
const timeout = new Timeout(
|
|
121
|
+
const timeout = new Timeout(
|
|
122
|
+
() => action(),
|
|
123
|
+
500,
|
|
124
|
+
SchedulePolicy.OnDemand,
|
|
125
|
+
);
|
|
114
126
|
timeout.set();
|
|
115
|
-
|
|
116
|
-
// $FlowFixMe[prop-missing]
|
|
117
|
-
const scheduledAction = setTimeout.mock.calls[0][0];
|
|
127
|
+
const scheduledAction = timeoutSpy.mock.calls[0][0];
|
|
118
128
|
|
|
119
129
|
// Act
|
|
120
130
|
scheduledAction();
|
|
@@ -139,16 +149,17 @@ describe("Timeout", () => {
|
|
|
139
149
|
|
|
140
150
|
it("should set the timeout again if it has already executed", () => {
|
|
141
151
|
// Arrange
|
|
152
|
+
const timeoutSpy = jest.spyOn(global, "setTimeout");
|
|
142
153
|
const action = jest.fn();
|
|
143
|
-
const timeout = new Timeout(action, 500,
|
|
154
|
+
const timeout = new Timeout(action, 500, SchedulePolicy.OnDemand);
|
|
144
155
|
timeout.set();
|
|
145
|
-
jest.
|
|
156
|
+
jest.runAllTimers();
|
|
146
157
|
|
|
147
158
|
// Act
|
|
148
159
|
timeout.set();
|
|
149
160
|
|
|
150
161
|
// Assert
|
|
151
|
-
expect(
|
|
162
|
+
expect(timeoutSpy).toHaveBeenCalledTimes(2);
|
|
152
163
|
});
|
|
153
164
|
});
|
|
154
165
|
|
|
@@ -167,27 +178,31 @@ describe("Timeout", () => {
|
|
|
167
178
|
expect(action).not.toHaveBeenCalled();
|
|
168
179
|
});
|
|
169
180
|
|
|
170
|
-
it("should invoke the action if
|
|
181
|
+
it("should invoke the action if clear policy is ClearPolicy.Resolve", () => {
|
|
171
182
|
// Arrange
|
|
172
183
|
const action = jest.fn();
|
|
173
184
|
const timeout = new Timeout(action, 500);
|
|
174
185
|
timeout.set();
|
|
175
186
|
|
|
176
187
|
// Act
|
|
177
|
-
timeout.clear(
|
|
188
|
+
timeout.clear(ClearPolicy.Resolve);
|
|
178
189
|
jest.runOnlyPendingTimers();
|
|
179
190
|
|
|
180
191
|
// Assert
|
|
181
192
|
expect(action).toHaveBeenCalledTimes(1);
|
|
182
193
|
});
|
|
183
194
|
|
|
184
|
-
it("should not invoke the action if
|
|
195
|
+
it("should not invoke the action if clear policy is ClearPolicy.Cancel", () => {
|
|
185
196
|
// Arrange
|
|
186
197
|
const action = jest.fn();
|
|
187
|
-
const timeout = new Timeout(
|
|
198
|
+
const timeout = new Timeout(
|
|
199
|
+
action,
|
|
200
|
+
500,
|
|
201
|
+
SchedulePolicy.Immediately,
|
|
202
|
+
);
|
|
188
203
|
|
|
189
204
|
// Act
|
|
190
|
-
timeout.clear(
|
|
205
|
+
timeout.clear(ClearPolicy.Cancel);
|
|
191
206
|
jest.runOnlyPendingTimers();
|
|
192
207
|
|
|
193
208
|
// Assert
|
|
@@ -197,10 +212,10 @@ describe("Timeout", () => {
|
|
|
197
212
|
it("should not invoke the action if timeout is not pending", () => {
|
|
198
213
|
// Arrange
|
|
199
214
|
const action = jest.fn();
|
|
200
|
-
const timeout = new Timeout(action, 500,
|
|
215
|
+
const timeout = new Timeout(action, 500, SchedulePolicy.OnDemand);
|
|
201
216
|
|
|
202
217
|
// Act
|
|
203
|
-
timeout.clear(
|
|
218
|
+
timeout.clear(ClearPolicy.Resolve);
|
|
204
219
|
jest.runOnlyPendingTimers();
|
|
205
220
|
|
|
206
221
|
// Assert
|
|
@@ -8,6 +8,7 @@ import type {
|
|
|
8
8
|
IInterval,
|
|
9
9
|
ITimeout,
|
|
10
10
|
IScheduleActions,
|
|
11
|
+
Options,
|
|
11
12
|
} from "./types.js";
|
|
12
13
|
|
|
13
14
|
/**
|
|
@@ -17,38 +18,53 @@ import type {
|
|
|
17
18
|
* `IScheduleActions` instance.
|
|
18
19
|
*/
|
|
19
20
|
export default class ActionScheduler implements IScheduleActions {
|
|
21
|
+
_disabled: boolean = false;
|
|
20
22
|
_registeredActions: Array<() => void> = [];
|
|
23
|
+
static +NoopAction: ITimeout & IAnimationFrame & IInterval = {
|
|
24
|
+
set: () => {},
|
|
25
|
+
get isSet() {
|
|
26
|
+
return false;
|
|
27
|
+
},
|
|
28
|
+
clear: () => {},
|
|
29
|
+
};
|
|
21
30
|
|
|
22
|
-
timeout(
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
const timeout = new Timeout(action, period, autoSchedule);
|
|
29
|
-
this._registeredActions.push(() => timeout.clear(resolveOnClear));
|
|
31
|
+
timeout(action: () => mixed, period: number, options?: Options): ITimeout {
|
|
32
|
+
if (this._disabled) {
|
|
33
|
+
return ActionScheduler.NoopAction;
|
|
34
|
+
}
|
|
35
|
+
const timeout = new Timeout(action, period, options?.schedulePolicy);
|
|
36
|
+
this._registeredActions.push(() => timeout.clear(options?.clearPolicy));
|
|
30
37
|
return timeout;
|
|
31
38
|
}
|
|
32
39
|
|
|
33
40
|
interval(
|
|
34
41
|
action: () => mixed,
|
|
35
42
|
period: number,
|
|
36
|
-
|
|
37
|
-
resolveOnClear?: boolean,
|
|
43
|
+
options?: Options,
|
|
38
44
|
): IInterval {
|
|
39
|
-
|
|
40
|
-
|
|
45
|
+
if (this._disabled) {
|
|
46
|
+
return ActionScheduler.NoopAction;
|
|
47
|
+
}
|
|
48
|
+
const interval = new Interval(action, period, options?.schedulePolicy);
|
|
49
|
+
this._registeredActions.push(() =>
|
|
50
|
+
interval.clear(options?.clearPolicy),
|
|
51
|
+
);
|
|
41
52
|
return interval;
|
|
42
53
|
}
|
|
43
54
|
|
|
44
55
|
animationFrame(
|
|
45
56
|
action: (DOMHighResTimeStamp) => void,
|
|
46
|
-
|
|
47
|
-
resolveOnClear?: boolean,
|
|
57
|
+
options?: Options,
|
|
48
58
|
): IAnimationFrame {
|
|
49
|
-
|
|
59
|
+
if (this._disabled) {
|
|
60
|
+
return ActionScheduler.NoopAction;
|
|
61
|
+
}
|
|
62
|
+
const animationFrame = new AnimationFrame(
|
|
63
|
+
action,
|
|
64
|
+
options?.schedulePolicy,
|
|
65
|
+
);
|
|
50
66
|
this._registeredActions.push(() =>
|
|
51
|
-
animationFrame.clear(
|
|
67
|
+
animationFrame.clear(options?.clearPolicy),
|
|
52
68
|
);
|
|
53
69
|
return animationFrame;
|
|
54
70
|
}
|
|
@@ -58,4 +74,13 @@ export default class ActionScheduler implements IScheduleActions {
|
|
|
58
74
|
this._registeredActions = [];
|
|
59
75
|
registered.forEach((clearFn) => clearFn());
|
|
60
76
|
}
|
|
77
|
+
|
|
78
|
+
/**
|
|
79
|
+
* Prevents this scheduler from creating any additional actions.
|
|
80
|
+
* This also clears any pending actions.
|
|
81
|
+
*/
|
|
82
|
+
disable(): void {
|
|
83
|
+
this._disabled = true;
|
|
84
|
+
this.clearAll();
|
|
85
|
+
}
|
|
61
86
|
}
|
|
@@ -1,5 +1,10 @@
|
|
|
1
1
|
// @flow
|
|
2
|
-
import
|
|
2
|
+
import {
|
|
3
|
+
SchedulePolicy as SchedulePolicies,
|
|
4
|
+
ClearPolicy as ClearPolicies,
|
|
5
|
+
} from "./policies.js";
|
|
6
|
+
|
|
7
|
+
import type {IAnimationFrame, SchedulePolicy, ClearPolicy} from "./types.js";
|
|
3
8
|
|
|
4
9
|
/**
|
|
5
10
|
* Encapsulates everything associated with calling requestAnimationFrame/
|
|
@@ -19,14 +24,15 @@ export default class AnimationFrame implements IAnimationFrame {
|
|
|
19
24
|
* The request is not made until set is called.
|
|
20
25
|
*
|
|
21
26
|
* @param {DOMHighResTimeStamp => mixed} action The action to be invoked.
|
|
22
|
-
* @param {
|
|
23
|
-
*
|
|
24
|
-
*
|
|
27
|
+
* @param {SchedulePolicy} [schedulePolicy] When SchedulePolicy.Immediately,
|
|
28
|
+
* the interval is set immediately on instantiation; otherwise, `set` must be
|
|
29
|
+
* called to set the interval.
|
|
30
|
+
* Defaults to `SchedulePolicy.Immediately`.
|
|
25
31
|
* @memberof AnimationFrame
|
|
26
32
|
*/
|
|
27
33
|
constructor(
|
|
28
34
|
action: (DOMHighResTimeStamp) => mixed,
|
|
29
|
-
|
|
35
|
+
schedulePolicy: SchedulePolicy = SchedulePolicies.Immediately,
|
|
30
36
|
) {
|
|
31
37
|
if (typeof action !== "function") {
|
|
32
38
|
throw new Error("Action must be a function");
|
|
@@ -34,7 +40,7 @@ export default class AnimationFrame implements IAnimationFrame {
|
|
|
34
40
|
|
|
35
41
|
this._action = action;
|
|
36
42
|
|
|
37
|
-
if (
|
|
43
|
+
if (schedulePolicy === SchedulePolicies.Immediately) {
|
|
38
44
|
this.set();
|
|
39
45
|
}
|
|
40
46
|
}
|
|
@@ -61,10 +67,10 @@ export default class AnimationFrame implements IAnimationFrame {
|
|
|
61
67
|
*/
|
|
62
68
|
set(): void {
|
|
63
69
|
if (this.isSet) {
|
|
64
|
-
this.clear(
|
|
70
|
+
this.clear(ClearPolicies.Cancel);
|
|
65
71
|
}
|
|
66
72
|
this._animationFrameId = requestAnimationFrame((time) =>
|
|
67
|
-
this.clear(
|
|
73
|
+
this.clear(ClearPolicies.Resolve, time),
|
|
68
74
|
);
|
|
69
75
|
}
|
|
70
76
|
|
|
@@ -74,21 +80,28 @@ export default class AnimationFrame implements IAnimationFrame {
|
|
|
74
80
|
* If the request is pending, this cancels that pending request without
|
|
75
81
|
* invoking the action. If no request is pending, this does nothing.
|
|
76
82
|
*
|
|
77
|
-
* @param {
|
|
78
|
-
* called, the request action is invoked after cancelling
|
|
79
|
-
*
|
|
83
|
+
* @param {ClearPolicy} [policy] When ClearPolicy.Resolve, if the request
|
|
84
|
+
* was set when called, the request action is invoked after cancelling
|
|
85
|
+
* the request; otherwise, the pending action is cancelled.
|
|
86
|
+
* Defaults to `ClearPolicy.Cancel`.
|
|
87
|
+
* @param {DOMHighResTimeStamp} [time] Timestamp to pass to the action when
|
|
88
|
+
* ClearPolicy.Resolve is specified. Ignored when ClearPolicy.Cancel is
|
|
89
|
+
* specified.
|
|
80
90
|
*
|
|
81
91
|
* @returns {void}
|
|
82
92
|
* @memberof AnimationFrame
|
|
83
93
|
*/
|
|
84
|
-
clear(
|
|
94
|
+
clear(
|
|
95
|
+
policy: ClearPolicy = ClearPolicies.Cancel,
|
|
96
|
+
time?: DOMHighResTimeStamp,
|
|
97
|
+
): void {
|
|
85
98
|
const animationFrameId = this._animationFrameId;
|
|
86
99
|
this._animationFrameId = null;
|
|
87
100
|
if (animationFrameId == null) {
|
|
88
101
|
return;
|
|
89
102
|
}
|
|
90
103
|
cancelAnimationFrame(animationFrameId);
|
|
91
|
-
if (
|
|
104
|
+
if (policy === ClearPolicies.Resolve) {
|
|
92
105
|
this._action(time || performance.now());
|
|
93
106
|
}
|
|
94
107
|
}
|
package/src/util/interval.js
CHANGED
|
@@ -1,5 +1,10 @@
|
|
|
1
1
|
// @flow
|
|
2
|
-
import
|
|
2
|
+
import {
|
|
3
|
+
SchedulePolicy as SchedulePolicies,
|
|
4
|
+
ClearPolicy as ClearPolicies,
|
|
5
|
+
} from "./policies.js";
|
|
6
|
+
|
|
7
|
+
import type {IInterval, SchedulePolicy, ClearPolicy} from "./types.js";
|
|
3
8
|
|
|
4
9
|
/**
|
|
5
10
|
* Encapsulates everything associated with calling setInterval/clearInterval,
|
|
@@ -22,16 +27,16 @@ export default class Interval implements IInterval {
|
|
|
22
27
|
* @param {() => mixed} action The action to be invoked each time the
|
|
23
28
|
* interval period has passed.
|
|
24
29
|
* @param {number} intervalMs The interval period.
|
|
25
|
-
* @param {
|
|
26
|
-
* immediately on
|
|
27
|
-
*
|
|
28
|
-
* Defaults to `
|
|
30
|
+
* @param {SchedulePolicy} [schedulePolicy] When SchedulePolicy.Immediately,
|
|
31
|
+
* the interval is set immediately on instantiation; otherwise, `set` must be
|
|
32
|
+
* called to set the interval.
|
|
33
|
+
* Defaults to `SchedulePolicy.Immediately`.
|
|
29
34
|
* @memberof Interval
|
|
30
35
|
*/
|
|
31
36
|
constructor(
|
|
32
37
|
action: () => mixed,
|
|
33
38
|
intervalMs: number,
|
|
34
|
-
|
|
39
|
+
schedulePolicy: SchedulePolicy = SchedulePolicies.Immediately,
|
|
35
40
|
) {
|
|
36
41
|
if (typeof action !== "function") {
|
|
37
42
|
throw new Error("Action must be a function");
|
|
@@ -44,7 +49,7 @@ export default class Interval implements IInterval {
|
|
|
44
49
|
this._action = action;
|
|
45
50
|
this._intervalMs = intervalMs;
|
|
46
51
|
|
|
47
|
-
if (
|
|
52
|
+
if (schedulePolicy === SchedulePolicies.Immediately) {
|
|
48
53
|
this.set();
|
|
49
54
|
}
|
|
50
55
|
}
|
|
@@ -69,7 +74,7 @@ export default class Interval implements IInterval {
|
|
|
69
74
|
*/
|
|
70
75
|
set(): void {
|
|
71
76
|
if (this.isSet) {
|
|
72
|
-
this.clear(
|
|
77
|
+
this.clear(ClearPolicies.Cancel);
|
|
73
78
|
}
|
|
74
79
|
this._intervalId = setInterval(() => this._action(), this._intervalMs);
|
|
75
80
|
}
|
|
@@ -80,21 +85,22 @@ export default class Interval implements IInterval {
|
|
|
80
85
|
* If the interval is active, this cancels that interval. If no interval is
|
|
81
86
|
* pending, this does nothing.
|
|
82
87
|
*
|
|
83
|
-
* @param {
|
|
84
|
-
* called, the
|
|
85
|
-
*
|
|
88
|
+
* @param {ClearPolicy} [policy] When ClearPolicy.Resolve, if the request
|
|
89
|
+
* was set when called, the request action is invoked after cancelling
|
|
90
|
+
* the request; otherwise, the pending action is cancelled.
|
|
91
|
+
* Defaults to `ClearPolicy.Cancel`.
|
|
86
92
|
*
|
|
87
93
|
* @returns {void}
|
|
88
94
|
* @memberof Interval
|
|
89
95
|
*/
|
|
90
|
-
clear(
|
|
96
|
+
clear(policy: ClearPolicy = ClearPolicies.Cancel): void {
|
|
91
97
|
const intervalId = this._intervalId;
|
|
92
98
|
this._intervalId = null;
|
|
93
99
|
if (intervalId == null) {
|
|
94
100
|
return;
|
|
95
101
|
}
|
|
96
102
|
clearInterval(intervalId);
|
|
97
|
-
if (
|
|
103
|
+
if (policy === ClearPolicies.Resolve) {
|
|
98
104
|
this._action();
|
|
99
105
|
}
|
|
100
106
|
}
|