@lytjs/common-scheduler 6.0.0
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/index.cjs +208 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.mts +81 -0
- package/dist/index.d.ts +81 -0
- package/dist/index.mjs +194 -0
- package/dist/index.mjs.map +1 -0
- package/package.json +38 -0
- package/src/env.d.ts +4 -0
- package/src/index.ts +328 -0
package/dist/index.cjs
ADDED
|
@@ -0,0 +1,208 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
// src/index.ts
|
|
4
|
+
var Priority = {
|
|
5
|
+
IDLE: 1e3,
|
|
6
|
+
// 空闲任务
|
|
7
|
+
LOW: 500,
|
|
8
|
+
// 低优先级
|
|
9
|
+
NORMAL: 0,
|
|
10
|
+
// 普通优先级(默认)
|
|
11
|
+
HIGH: -500,
|
|
12
|
+
// 高优先级
|
|
13
|
+
CRITICAL: -1e3
|
|
14
|
+
// 关键任务
|
|
15
|
+
};
|
|
16
|
+
var globalErrorHandler = null;
|
|
17
|
+
function setErrorHandler(handler) {
|
|
18
|
+
globalErrorHandler = handler;
|
|
19
|
+
}
|
|
20
|
+
var isFlushing = false;
|
|
21
|
+
var isFlushPending = false;
|
|
22
|
+
var DEFAULT_MAX_ITERATIONS = 100;
|
|
23
|
+
var maxIterations = DEFAULT_MAX_ITERATIONS;
|
|
24
|
+
function setMaxIterations(n) {
|
|
25
|
+
if (typeof n === "number" && n > 0) {
|
|
26
|
+
maxIterations = n;
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
var queue = [];
|
|
30
|
+
var queueSet = /* @__PURE__ */ new Set();
|
|
31
|
+
var preFlushCbs = [];
|
|
32
|
+
var preFlushCbsSet = /* @__PURE__ */ new Set();
|
|
33
|
+
var postFlushCbs = [];
|
|
34
|
+
var postFlushCbsSet = /* @__PURE__ */ new Set();
|
|
35
|
+
var resolvedPromise = null;
|
|
36
|
+
function getResolvedPromise() {
|
|
37
|
+
return resolvedPromise ?? (resolvedPromise = Promise.resolve());
|
|
38
|
+
}
|
|
39
|
+
function queueJob(job) {
|
|
40
|
+
if (!queueSet.has(job)) {
|
|
41
|
+
queueSet.add(job);
|
|
42
|
+
queue.push(job);
|
|
43
|
+
queueFlush();
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
function queueJobWithPriority(job) {
|
|
47
|
+
if (!queueSet.has(job)) {
|
|
48
|
+
queueSet.add(job);
|
|
49
|
+
const priority = job.priority ?? Priority.NORMAL;
|
|
50
|
+
let insertIndex = queue.length;
|
|
51
|
+
for (let i = 0; i < queue.length; i++) {
|
|
52
|
+
const existingJob = queue[i];
|
|
53
|
+
const existingPriority = existingJob?.priority ?? Priority.NORMAL;
|
|
54
|
+
if (existingPriority > priority) {
|
|
55
|
+
insertIndex = i;
|
|
56
|
+
break;
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
queue.splice(insertIndex, 0, job);
|
|
60
|
+
queueFlush();
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
function queuePreFlushCb(cb) {
|
|
64
|
+
if (!preFlushCbsSet.has(cb)) {
|
|
65
|
+
preFlushCbsSet.add(cb);
|
|
66
|
+
preFlushCbs.push(cb);
|
|
67
|
+
queueFlush();
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
function queuePostFlushCb(cb) {
|
|
71
|
+
if (!postFlushCbsSet.has(cb)) {
|
|
72
|
+
postFlushCbsSet.add(cb);
|
|
73
|
+
postFlushCbs.push(cb);
|
|
74
|
+
queueFlush();
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
function nextTick(cb) {
|
|
78
|
+
const p = getResolvedPromise();
|
|
79
|
+
return cb ? p.then(cb) : p;
|
|
80
|
+
}
|
|
81
|
+
function flushJobs() {
|
|
82
|
+
isFlushing = true;
|
|
83
|
+
isFlushPending = false;
|
|
84
|
+
let iterations = 0;
|
|
85
|
+
try {
|
|
86
|
+
while ((preFlushCbs.length > 0 || queue.length > 0 || postFlushCbs.length > 0) && iterations < maxIterations) {
|
|
87
|
+
iterations++;
|
|
88
|
+
for (let i = 0; i < preFlushCbs.length; i++) {
|
|
89
|
+
const cb = preFlushCbs[i];
|
|
90
|
+
preFlushCbsSet.delete(cb);
|
|
91
|
+
try {
|
|
92
|
+
cb();
|
|
93
|
+
} catch (e) {
|
|
94
|
+
if (globalErrorHandler) globalErrorHandler(e, "pre-flush callback");
|
|
95
|
+
if (__DEV__) {
|
|
96
|
+
console.error("[LytJS] pre-flush callback failed:", e);
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
preFlushCbs.length = 0;
|
|
101
|
+
flushQueueByPriority();
|
|
102
|
+
for (let i = 0; i < postFlushCbs.length; i++) {
|
|
103
|
+
const cb = postFlushCbs[i];
|
|
104
|
+
postFlushCbsSet.delete(cb);
|
|
105
|
+
try {
|
|
106
|
+
cb();
|
|
107
|
+
} catch (e) {
|
|
108
|
+
if (globalErrorHandler) globalErrorHandler(e, "post-flush callback");
|
|
109
|
+
if (__DEV__) {
|
|
110
|
+
console.error("[LytJS] post-flush callback failed:", e);
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
postFlushCbs.length = 0;
|
|
115
|
+
}
|
|
116
|
+
if (iterations >= maxIterations) {
|
|
117
|
+
const remainingJobs = queue.length + preFlushCbs.length + postFlushCbs.length;
|
|
118
|
+
const msg = `[LytJS] flushJobs exceeded ${maxIterations} iterations. Possible infinite update loop detected.` + (remainingJobs > 0 ? ` ${remainingJobs} job(s) were discarded.` : "");
|
|
119
|
+
if (__DEV__) {
|
|
120
|
+
console.warn(msg);
|
|
121
|
+
} else {
|
|
122
|
+
console.error(msg);
|
|
123
|
+
}
|
|
124
|
+
queue.length = 0;
|
|
125
|
+
queueSet.clear();
|
|
126
|
+
preFlushCbs.length = 0;
|
|
127
|
+
preFlushCbsSet.clear();
|
|
128
|
+
postFlushCbs.length = 0;
|
|
129
|
+
postFlushCbsSet.clear();
|
|
130
|
+
}
|
|
131
|
+
} finally {
|
|
132
|
+
isFlushing = false;
|
|
133
|
+
if (preFlushCbs.length || queue.length || postFlushCbs.length) {
|
|
134
|
+
isFlushPending = true;
|
|
135
|
+
nextTick(flushJobs);
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
function flushQueueByPriority() {
|
|
140
|
+
const HIGH_THRESHOLD = Priority.NORMAL;
|
|
141
|
+
const executeBatch = (minPriority, maxPriority) => {
|
|
142
|
+
let i = 0;
|
|
143
|
+
while (i < queue.length) {
|
|
144
|
+
const job = queue[i];
|
|
145
|
+
const priority = job?.priority ?? Priority.NORMAL;
|
|
146
|
+
if (priority >= minPriority && priority <= maxPriority) {
|
|
147
|
+
queue.splice(i, 1);
|
|
148
|
+
queueSet.delete(job);
|
|
149
|
+
try {
|
|
150
|
+
job();
|
|
151
|
+
} catch (e) {
|
|
152
|
+
if (globalErrorHandler) globalErrorHandler(e, "job execution");
|
|
153
|
+
if (__DEV__) {
|
|
154
|
+
console.error("[LytJS] job execution failed:", e);
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
} else {
|
|
158
|
+
i++;
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
};
|
|
162
|
+
executeBatch(Priority.CRITICAL, HIGH_THRESHOLD);
|
|
163
|
+
executeBatch(Priority.NORMAL, Priority.LOW - 1);
|
|
164
|
+
executeBatch(Priority.LOW, Priority.IDLE);
|
|
165
|
+
}
|
|
166
|
+
function flushSync() {
|
|
167
|
+
if (isFlushing) return;
|
|
168
|
+
flushJobs();
|
|
169
|
+
}
|
|
170
|
+
function hasPendingJobs() {
|
|
171
|
+
return preFlushCbs.length > 0 || queue.length > 0 || postFlushCbs.length > 0;
|
|
172
|
+
}
|
|
173
|
+
function getPendingJobCount() {
|
|
174
|
+
return queue.length;
|
|
175
|
+
}
|
|
176
|
+
function queueFlush() {
|
|
177
|
+
if (!isFlushing && !isFlushPending) {
|
|
178
|
+
isFlushPending = true;
|
|
179
|
+
nextTick(flushJobs);
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
function resetSchedulerState() {
|
|
183
|
+
queue.length = 0;
|
|
184
|
+
queueSet.clear();
|
|
185
|
+
preFlushCbs.length = 0;
|
|
186
|
+
preFlushCbsSet.clear();
|
|
187
|
+
postFlushCbs.length = 0;
|
|
188
|
+
postFlushCbsSet.clear();
|
|
189
|
+
isFlushing = false;
|
|
190
|
+
isFlushPending = false;
|
|
191
|
+
maxIterations = DEFAULT_MAX_ITERATIONS;
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
exports.Priority = Priority;
|
|
195
|
+
exports.flushJobs = flushJobs;
|
|
196
|
+
exports.flushSync = flushSync;
|
|
197
|
+
exports.getPendingJobCount = getPendingJobCount;
|
|
198
|
+
exports.hasPendingJobs = hasPendingJobs;
|
|
199
|
+
exports.nextTick = nextTick;
|
|
200
|
+
exports.queueJob = queueJob;
|
|
201
|
+
exports.queueJobWithPriority = queueJobWithPriority;
|
|
202
|
+
exports.queuePostFlushCb = queuePostFlushCb;
|
|
203
|
+
exports.queuePreFlushCb = queuePreFlushCb;
|
|
204
|
+
exports.resetSchedulerState = resetSchedulerState;
|
|
205
|
+
exports.setErrorHandler = setErrorHandler;
|
|
206
|
+
exports.setMaxIterations = setMaxIterations;
|
|
207
|
+
//# sourceMappingURL=index.cjs.map
|
|
208
|
+
//# sourceMappingURL=index.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/index.ts"],"names":[],"mappings":"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","file":"index.cjs","sourcesContent":["/**\r\n * @lytjs/common-scheduler\r\n * 任务调度器 - 管理异步任务队列\r\n * 支持 pre-flush 和 post-flush 两种队列\r\n */\r\n\r\n// ============================================================\r\n// 调度器类型\r\n// ============================================================\r\n\r\n/**\r\n * 调度器任务类型\r\n */\r\nexport type SchedulerJob = () => void;\r\n\r\n/**\r\n * 带优先级的调度器任务类型\r\n */\r\nexport interface SchedulerJobWithPriority extends SchedulerJob {\r\n /** 优先级,数值越小优先级越高 */\r\n priority?: number;\r\n}\r\n\r\n/**\r\n * 优先级常量\r\n */\r\nexport const Priority = {\r\n IDLE: 1000, // 空闲任务\r\n LOW: 500, // 低优先级\r\n NORMAL: 0, // 普通优先级(默认)\r\n HIGH: -500, // 高优先级\r\n CRITICAL: -1000, // 关键任务\r\n} as const;\r\n\r\n// ============================================================\r\n// 全局错误处理\r\n// ============================================================\r\n\r\nlet globalErrorHandler: ((error: Error, info: string) => void) | null = null;\r\nexport function setErrorHandler(handler: ((error: Error, info: string) => void) | null): void {\r\n globalErrorHandler = handler;\r\n}\r\n\r\n// ============================================================\r\n// 调度器状态\r\n// ============================================================\r\n\r\nlet isFlushing = false;\r\nlet isFlushPending = false;\r\n\r\n/** Default maximum iterations for flushJobs loop to prevent infinite update loops. */\r\nconst DEFAULT_MAX_ITERATIONS = 100;\r\n\r\nlet maxIterations = DEFAULT_MAX_ITERATIONS;\r\n\r\n/**\r\n * 设置 flushJobs 的最大迭代次数\r\n * 用于自定义调度器在复杂场景下的循环上限\r\n */\r\nexport function setMaxIterations(n: number): void {\r\n if (typeof n === 'number' && n > 0) {\r\n maxIterations = n;\r\n }\r\n}\r\n\r\nconst queue: SchedulerJob[] = [];\r\nconst queueSet: Set<SchedulerJob> = new Set();\r\nconst preFlushCbs: SchedulerJob[] = [];\r\nconst preFlushCbsSet: Set<SchedulerJob> = new Set();\r\nconst postFlushCbs: SchedulerJob[] = [];\r\nconst postFlushCbsSet: Set<SchedulerJob> = new Set();\r\n\r\nlet resolvedPromise: Promise<void> | null = null;\r\n\r\n/**\r\n * 获取当前环境的 resolvedPromise\r\n */\r\nfunction getResolvedPromise(): Promise<void> {\r\n return (resolvedPromise ??= Promise.resolve());\r\n}\r\n\r\n// ============================================================\r\n// 公共 API\r\n// ============================================================\r\n\r\n/**\r\n * 将任务加入队列\r\n * 同一个任务(引用相同)只会被加入一次\r\n */\r\nexport function queueJob(job: SchedulerJob): void {\r\n if (!queueSet.has(job)) {\r\n queueSet.add(job);\r\n queue.push(job);\r\n queueFlush();\r\n }\r\n}\r\n\r\n/**\r\n * 将带优先级的任务加入队列\r\n * 按优先级插入(优先级高的在前),相同优先级保持插入顺序(稳定排序)\r\n */\r\nexport function queueJobWithPriority(job: SchedulerJobWithPriority): void {\r\n if (!queueSet.has(job)) {\r\n queueSet.add(job);\r\n const priority = job.priority ?? Priority.NORMAL;\r\n // 找到第一个优先级大于等于当前 job 的位置(即当前 job 应该插入的位置)\r\n // 这样优先级高的(数值小的)排在前面,相同优先级的保持插入顺序\r\n let insertIndex = queue.length;\r\n for (let i = 0; i < queue.length; i++) {\r\n const existingJob = queue[i] as SchedulerJobWithPriority | undefined;\r\n const existingPriority = existingJob?.priority ?? Priority.NORMAL;\r\n if (existingPriority > priority) {\r\n insertIndex = i;\r\n break;\r\n }\r\n }\r\n queue.splice(insertIndex, 0, job);\r\n queueFlush();\r\n }\r\n}\r\n\r\n/**\r\n * 将回调加入 pre-flush 队列\r\n * 在主 job 队列之前执行(用于 watch 的 \"pre\" 模式)\r\n */\r\nexport function queuePreFlushCb(cb: SchedulerJob): void {\r\n if (!preFlushCbsSet.has(cb)) {\r\n preFlushCbsSet.add(cb);\r\n preFlushCbs.push(cb);\r\n queueFlush();\r\n }\r\n}\r\n\r\n/**\r\n * 将回调加入 post-flush 队列\r\n * 在所有 job 执行完毕后执行\r\n */\r\nexport function queuePostFlushCb(cb: SchedulerJob): void {\r\n if (!postFlushCbsSet.has(cb)) {\r\n postFlushCbsSet.add(cb);\r\n postFlushCbs.push(cb);\r\n queueFlush();\r\n }\r\n}\r\n\r\n/**\r\n * 在下一个 tick 执行回调\r\n */\r\nexport function nextTick(cb?: SchedulerJob): Promise<void> {\r\n const p = getResolvedPromise();\r\n return cb ? p.then(cb) : p;\r\n}\r\n\r\n/**\r\n * 刷新所有待执行的任务\r\n * 执行顺序:pre-flush 回调 -> 主 job 队列(按优先级) -> post-flush 回调\r\n * 支持循环处理:执行期间新增的回调也会被处理\r\n * 优先级策略:每轮先执行 CRITICAL/HIGH,再 NORMAL,最后 LOW/IDLE\r\n */\r\nexport function flushJobs(): void {\r\n isFlushing = true;\r\n isFlushPending = false;\r\n\r\n let iterations = 0;\r\n\r\n try {\r\n // 循环处理:执行期间新增的回调也会被处理\r\n while (\r\n (preFlushCbs.length > 0 || queue.length > 0 || postFlushCbs.length > 0) &&\r\n iterations < maxIterations\r\n ) {\r\n iterations++;\r\n\r\n // 1. 执行 pre-flush 队列(watch pre 模式回调)\r\n for (let i = 0; i < preFlushCbs.length; i++) {\r\n const cb = preFlushCbs[i]!;\r\n preFlushCbsSet.delete(cb);\r\n try {\r\n cb();\r\n } catch (e) {\r\n if (globalErrorHandler) globalErrorHandler(e as Error, 'pre-flush callback');\r\n if (__DEV__) {\r\n console.error('[LytJS] pre-flush callback failed:', e);\r\n }\r\n }\r\n }\r\n preFlushCbs.length = 0;\r\n\r\n // 2. 执行主 job 队列(按优先级分组执行)\r\n flushQueueByPriority();\r\n\r\n // 3. 执行 post-flush 回调\r\n for (let i = 0; i < postFlushCbs.length; i++) {\r\n const cb = postFlushCbs[i]!;\r\n postFlushCbsSet.delete(cb);\r\n try {\r\n cb();\r\n } catch (e) {\r\n if (globalErrorHandler) globalErrorHandler(e as Error, 'post-flush callback');\r\n if (__DEV__) {\r\n console.error('[LytJS] post-flush callback failed:', e);\r\n }\r\n }\r\n }\r\n postFlushCbs.length = 0;\r\n }\r\n\r\n if (iterations >= maxIterations) {\r\n const remainingJobs = queue.length + preFlushCbs.length + postFlushCbs.length;\r\n const msg =\r\n `[LytJS] flushJobs exceeded ${maxIterations} iterations. ` +\r\n `Possible infinite update loop detected.` +\r\n (remainingJobs > 0 ? ` ${remainingJobs} job(s) were discarded.` : '');\r\n if (__DEV__) {\r\n console.warn(msg);\r\n } else {\r\n console.error(msg);\r\n }\r\n // Clear all queues to prevent re-triggering\r\n queue.length = 0;\r\n queueSet.clear();\r\n preFlushCbs.length = 0;\r\n preFlushCbsSet.clear();\r\n postFlushCbs.length = 0;\r\n postFlushCbsSet.clear();\r\n }\r\n } finally {\r\n isFlushing = false;\r\n\r\n // If new jobs were queued during flush, schedule next flush via nextTick\r\n // instead of recursive call to avoid stack overflow\r\n if (preFlushCbs.length || queue.length || postFlushCbs.length) {\r\n isFlushPending = true;\r\n nextTick(flushJobs);\r\n }\r\n }\r\n}\r\n\r\n/**\r\n * 按优先级分组执行队列中的任务\r\n * 执行顺序:CRITICAL/HIGH -> NORMAL -> LOW/IDLE\r\n * 如果某轮执行中新增了高优先级任务,优先执行它们\r\n */\r\nfunction flushQueueByPriority(): void {\r\n // 优先级阈值定义\r\n const HIGH_THRESHOLD = Priority.NORMAL; // <= 0 视为高优先级(CRITICAL: -1000, HIGH: -500)\r\n\r\n // 分组执行,每执行一组后检查是否有新的高优先级任务加入\r\n const executeBatch = (minPriority: number, maxPriority: number): void => {\r\n let i = 0;\r\n while (i < queue.length) {\r\n const job = queue[i] as SchedulerJobWithPriority | undefined;\r\n const priority = job?.priority ?? Priority.NORMAL;\r\n if (priority >= minPriority && priority <= maxPriority) {\r\n // 移除并执行\r\n queue.splice(i, 1);\r\n queueSet.delete(job!);\r\n try {\r\n job!();\r\n } catch (e) {\r\n if (globalErrorHandler) globalErrorHandler(e as Error, 'job execution');\r\n if (__DEV__) {\r\n console.error('[LytJS] job execution failed:', e);\r\n }\r\n }\r\n // 不递增 i,因为 splice 后下一个元素移到了当前位置\r\n } else {\r\n i++;\r\n }\r\n }\r\n };\r\n\r\n // 第一轮:执行 CRITICAL 和 HIGH 任务(priority <= 0)\r\n executeBatch(Priority.CRITICAL, HIGH_THRESHOLD);\r\n\r\n // 第二轮:执行 NORMAL 任务(priority >= 0 && priority < 500)\r\n executeBatch(Priority.NORMAL, Priority.LOW - 1);\r\n\r\n // 第三轮:执行 LOW 和 IDLE 任务(priority >= 500)\r\n executeBatch(Priority.LOW, Priority.IDLE);\r\n}\r\n\r\n/**\r\n * 同步刷新所有任务\r\n */\r\nexport function flushSync(): void {\r\n if (isFlushing) return;\r\n flushJobs();\r\n}\r\n\r\n/**\r\n * 检查是否有待执行的任务\r\n */\r\nexport function hasPendingJobs(): boolean {\r\n return preFlushCbs.length > 0 || queue.length > 0 || postFlushCbs.length > 0;\r\n}\r\n\r\n/**\r\n * 获取待执行的 job 数量\r\n */\r\nexport function getPendingJobCount(): number {\r\n return queue.length;\r\n}\r\n\r\n/**\r\n * 调度一次 flush\r\n */\r\nfunction queueFlush(): void {\r\n if (!isFlushing && !isFlushPending) {\r\n isFlushPending = true;\r\n nextTick(flushJobs);\r\n }\r\n}\r\n\r\n/**\r\n * 重置调度器状态(用于测试)\r\n */\r\nexport function resetSchedulerState(): void {\r\n queue.length = 0;\r\n queueSet.clear();\r\n preFlushCbs.length = 0;\r\n preFlushCbsSet.clear();\r\n postFlushCbs.length = 0;\r\n postFlushCbsSet.clear();\r\n isFlushing = false;\r\n isFlushPending = false;\r\n maxIterations = DEFAULT_MAX_ITERATIONS;\r\n}\r\n"]}
|
package/dist/index.d.mts
ADDED
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @lytjs/common-scheduler
|
|
3
|
+
* 任务调度器 - 管理异步任务队列
|
|
4
|
+
* 支持 pre-flush 和 post-flush 两种队列
|
|
5
|
+
*/
|
|
6
|
+
/**
|
|
7
|
+
* 调度器任务类型
|
|
8
|
+
*/
|
|
9
|
+
type SchedulerJob = () => void;
|
|
10
|
+
/**
|
|
11
|
+
* 带优先级的调度器任务类型
|
|
12
|
+
*/
|
|
13
|
+
interface SchedulerJobWithPriority extends SchedulerJob {
|
|
14
|
+
/** 优先级,数值越小优先级越高 */
|
|
15
|
+
priority?: number;
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* 优先级常量
|
|
19
|
+
*/
|
|
20
|
+
declare const Priority: {
|
|
21
|
+
readonly IDLE: 1000;
|
|
22
|
+
readonly LOW: 500;
|
|
23
|
+
readonly NORMAL: 0;
|
|
24
|
+
readonly HIGH: -500;
|
|
25
|
+
readonly CRITICAL: -1000;
|
|
26
|
+
};
|
|
27
|
+
declare function setErrorHandler(handler: ((error: Error, info: string) => void) | null): void;
|
|
28
|
+
/**
|
|
29
|
+
* 设置 flushJobs 的最大迭代次数
|
|
30
|
+
* 用于自定义调度器在复杂场景下的循环上限
|
|
31
|
+
*/
|
|
32
|
+
declare function setMaxIterations(n: number): void;
|
|
33
|
+
/**
|
|
34
|
+
* 将任务加入队列
|
|
35
|
+
* 同一个任务(引用相同)只会被加入一次
|
|
36
|
+
*/
|
|
37
|
+
declare function queueJob(job: SchedulerJob): void;
|
|
38
|
+
/**
|
|
39
|
+
* 将带优先级的任务加入队列
|
|
40
|
+
* 按优先级插入(优先级高的在前),相同优先级保持插入顺序(稳定排序)
|
|
41
|
+
*/
|
|
42
|
+
declare function queueJobWithPriority(job: SchedulerJobWithPriority): void;
|
|
43
|
+
/**
|
|
44
|
+
* 将回调加入 pre-flush 队列
|
|
45
|
+
* 在主 job 队列之前执行(用于 watch 的 "pre" 模式)
|
|
46
|
+
*/
|
|
47
|
+
declare function queuePreFlushCb(cb: SchedulerJob): void;
|
|
48
|
+
/**
|
|
49
|
+
* 将回调加入 post-flush 队列
|
|
50
|
+
* 在所有 job 执行完毕后执行
|
|
51
|
+
*/
|
|
52
|
+
declare function queuePostFlushCb(cb: SchedulerJob): void;
|
|
53
|
+
/**
|
|
54
|
+
* 在下一个 tick 执行回调
|
|
55
|
+
*/
|
|
56
|
+
declare function nextTick(cb?: SchedulerJob): Promise<void>;
|
|
57
|
+
/**
|
|
58
|
+
* 刷新所有待执行的任务
|
|
59
|
+
* 执行顺序:pre-flush 回调 -> 主 job 队列(按优先级) -> post-flush 回调
|
|
60
|
+
* 支持循环处理:执行期间新增的回调也会被处理
|
|
61
|
+
* 优先级策略:每轮先执行 CRITICAL/HIGH,再 NORMAL,最后 LOW/IDLE
|
|
62
|
+
*/
|
|
63
|
+
declare function flushJobs(): void;
|
|
64
|
+
/**
|
|
65
|
+
* 同步刷新所有任务
|
|
66
|
+
*/
|
|
67
|
+
declare function flushSync(): void;
|
|
68
|
+
/**
|
|
69
|
+
* 检查是否有待执行的任务
|
|
70
|
+
*/
|
|
71
|
+
declare function hasPendingJobs(): boolean;
|
|
72
|
+
/**
|
|
73
|
+
* 获取待执行的 job 数量
|
|
74
|
+
*/
|
|
75
|
+
declare function getPendingJobCount(): number;
|
|
76
|
+
/**
|
|
77
|
+
* 重置调度器状态(用于测试)
|
|
78
|
+
*/
|
|
79
|
+
declare function resetSchedulerState(): void;
|
|
80
|
+
|
|
81
|
+
export { Priority, type SchedulerJob, type SchedulerJobWithPriority, flushJobs, flushSync, getPendingJobCount, hasPendingJobs, nextTick, queueJob, queueJobWithPriority, queuePostFlushCb, queuePreFlushCb, resetSchedulerState, setErrorHandler, setMaxIterations };
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @lytjs/common-scheduler
|
|
3
|
+
* 任务调度器 - 管理异步任务队列
|
|
4
|
+
* 支持 pre-flush 和 post-flush 两种队列
|
|
5
|
+
*/
|
|
6
|
+
/**
|
|
7
|
+
* 调度器任务类型
|
|
8
|
+
*/
|
|
9
|
+
type SchedulerJob = () => void;
|
|
10
|
+
/**
|
|
11
|
+
* 带优先级的调度器任务类型
|
|
12
|
+
*/
|
|
13
|
+
interface SchedulerJobWithPriority extends SchedulerJob {
|
|
14
|
+
/** 优先级,数值越小优先级越高 */
|
|
15
|
+
priority?: number;
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* 优先级常量
|
|
19
|
+
*/
|
|
20
|
+
declare const Priority: {
|
|
21
|
+
readonly IDLE: 1000;
|
|
22
|
+
readonly LOW: 500;
|
|
23
|
+
readonly NORMAL: 0;
|
|
24
|
+
readonly HIGH: -500;
|
|
25
|
+
readonly CRITICAL: -1000;
|
|
26
|
+
};
|
|
27
|
+
declare function setErrorHandler(handler: ((error: Error, info: string) => void) | null): void;
|
|
28
|
+
/**
|
|
29
|
+
* 设置 flushJobs 的最大迭代次数
|
|
30
|
+
* 用于自定义调度器在复杂场景下的循环上限
|
|
31
|
+
*/
|
|
32
|
+
declare function setMaxIterations(n: number): void;
|
|
33
|
+
/**
|
|
34
|
+
* 将任务加入队列
|
|
35
|
+
* 同一个任务(引用相同)只会被加入一次
|
|
36
|
+
*/
|
|
37
|
+
declare function queueJob(job: SchedulerJob): void;
|
|
38
|
+
/**
|
|
39
|
+
* 将带优先级的任务加入队列
|
|
40
|
+
* 按优先级插入(优先级高的在前),相同优先级保持插入顺序(稳定排序)
|
|
41
|
+
*/
|
|
42
|
+
declare function queueJobWithPriority(job: SchedulerJobWithPriority): void;
|
|
43
|
+
/**
|
|
44
|
+
* 将回调加入 pre-flush 队列
|
|
45
|
+
* 在主 job 队列之前执行(用于 watch 的 "pre" 模式)
|
|
46
|
+
*/
|
|
47
|
+
declare function queuePreFlushCb(cb: SchedulerJob): void;
|
|
48
|
+
/**
|
|
49
|
+
* 将回调加入 post-flush 队列
|
|
50
|
+
* 在所有 job 执行完毕后执行
|
|
51
|
+
*/
|
|
52
|
+
declare function queuePostFlushCb(cb: SchedulerJob): void;
|
|
53
|
+
/**
|
|
54
|
+
* 在下一个 tick 执行回调
|
|
55
|
+
*/
|
|
56
|
+
declare function nextTick(cb?: SchedulerJob): Promise<void>;
|
|
57
|
+
/**
|
|
58
|
+
* 刷新所有待执行的任务
|
|
59
|
+
* 执行顺序:pre-flush 回调 -> 主 job 队列(按优先级) -> post-flush 回调
|
|
60
|
+
* 支持循环处理:执行期间新增的回调也会被处理
|
|
61
|
+
* 优先级策略:每轮先执行 CRITICAL/HIGH,再 NORMAL,最后 LOW/IDLE
|
|
62
|
+
*/
|
|
63
|
+
declare function flushJobs(): void;
|
|
64
|
+
/**
|
|
65
|
+
* 同步刷新所有任务
|
|
66
|
+
*/
|
|
67
|
+
declare function flushSync(): void;
|
|
68
|
+
/**
|
|
69
|
+
* 检查是否有待执行的任务
|
|
70
|
+
*/
|
|
71
|
+
declare function hasPendingJobs(): boolean;
|
|
72
|
+
/**
|
|
73
|
+
* 获取待执行的 job 数量
|
|
74
|
+
*/
|
|
75
|
+
declare function getPendingJobCount(): number;
|
|
76
|
+
/**
|
|
77
|
+
* 重置调度器状态(用于测试)
|
|
78
|
+
*/
|
|
79
|
+
declare function resetSchedulerState(): void;
|
|
80
|
+
|
|
81
|
+
export { Priority, type SchedulerJob, type SchedulerJobWithPriority, flushJobs, flushSync, getPendingJobCount, hasPendingJobs, nextTick, queueJob, queueJobWithPriority, queuePostFlushCb, queuePreFlushCb, resetSchedulerState, setErrorHandler, setMaxIterations };
|
package/dist/index.mjs
ADDED
|
@@ -0,0 +1,194 @@
|
|
|
1
|
+
// src/index.ts
|
|
2
|
+
var Priority = {
|
|
3
|
+
IDLE: 1e3,
|
|
4
|
+
// 空闲任务
|
|
5
|
+
LOW: 500,
|
|
6
|
+
// 低优先级
|
|
7
|
+
NORMAL: 0,
|
|
8
|
+
// 普通优先级(默认)
|
|
9
|
+
HIGH: -500,
|
|
10
|
+
// 高优先级
|
|
11
|
+
CRITICAL: -1e3
|
|
12
|
+
// 关键任务
|
|
13
|
+
};
|
|
14
|
+
var globalErrorHandler = null;
|
|
15
|
+
function setErrorHandler(handler) {
|
|
16
|
+
globalErrorHandler = handler;
|
|
17
|
+
}
|
|
18
|
+
var isFlushing = false;
|
|
19
|
+
var isFlushPending = false;
|
|
20
|
+
var DEFAULT_MAX_ITERATIONS = 100;
|
|
21
|
+
var maxIterations = DEFAULT_MAX_ITERATIONS;
|
|
22
|
+
function setMaxIterations(n) {
|
|
23
|
+
if (typeof n === "number" && n > 0) {
|
|
24
|
+
maxIterations = n;
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
var queue = [];
|
|
28
|
+
var queueSet = /* @__PURE__ */ new Set();
|
|
29
|
+
var preFlushCbs = [];
|
|
30
|
+
var preFlushCbsSet = /* @__PURE__ */ new Set();
|
|
31
|
+
var postFlushCbs = [];
|
|
32
|
+
var postFlushCbsSet = /* @__PURE__ */ new Set();
|
|
33
|
+
var resolvedPromise = null;
|
|
34
|
+
function getResolvedPromise() {
|
|
35
|
+
return resolvedPromise ?? (resolvedPromise = Promise.resolve());
|
|
36
|
+
}
|
|
37
|
+
function queueJob(job) {
|
|
38
|
+
if (!queueSet.has(job)) {
|
|
39
|
+
queueSet.add(job);
|
|
40
|
+
queue.push(job);
|
|
41
|
+
queueFlush();
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
function queueJobWithPriority(job) {
|
|
45
|
+
if (!queueSet.has(job)) {
|
|
46
|
+
queueSet.add(job);
|
|
47
|
+
const priority = job.priority ?? Priority.NORMAL;
|
|
48
|
+
let insertIndex = queue.length;
|
|
49
|
+
for (let i = 0; i < queue.length; i++) {
|
|
50
|
+
const existingJob = queue[i];
|
|
51
|
+
const existingPriority = existingJob?.priority ?? Priority.NORMAL;
|
|
52
|
+
if (existingPriority > priority) {
|
|
53
|
+
insertIndex = i;
|
|
54
|
+
break;
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
queue.splice(insertIndex, 0, job);
|
|
58
|
+
queueFlush();
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
function queuePreFlushCb(cb) {
|
|
62
|
+
if (!preFlushCbsSet.has(cb)) {
|
|
63
|
+
preFlushCbsSet.add(cb);
|
|
64
|
+
preFlushCbs.push(cb);
|
|
65
|
+
queueFlush();
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
function queuePostFlushCb(cb) {
|
|
69
|
+
if (!postFlushCbsSet.has(cb)) {
|
|
70
|
+
postFlushCbsSet.add(cb);
|
|
71
|
+
postFlushCbs.push(cb);
|
|
72
|
+
queueFlush();
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
function nextTick(cb) {
|
|
76
|
+
const p = getResolvedPromise();
|
|
77
|
+
return cb ? p.then(cb) : p;
|
|
78
|
+
}
|
|
79
|
+
function flushJobs() {
|
|
80
|
+
isFlushing = true;
|
|
81
|
+
isFlushPending = false;
|
|
82
|
+
let iterations = 0;
|
|
83
|
+
try {
|
|
84
|
+
while ((preFlushCbs.length > 0 || queue.length > 0 || postFlushCbs.length > 0) && iterations < maxIterations) {
|
|
85
|
+
iterations++;
|
|
86
|
+
for (let i = 0; i < preFlushCbs.length; i++) {
|
|
87
|
+
const cb = preFlushCbs[i];
|
|
88
|
+
preFlushCbsSet.delete(cb);
|
|
89
|
+
try {
|
|
90
|
+
cb();
|
|
91
|
+
} catch (e) {
|
|
92
|
+
if (globalErrorHandler) globalErrorHandler(e, "pre-flush callback");
|
|
93
|
+
if (__DEV__) {
|
|
94
|
+
console.error("[LytJS] pre-flush callback failed:", e);
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
preFlushCbs.length = 0;
|
|
99
|
+
flushQueueByPriority();
|
|
100
|
+
for (let i = 0; i < postFlushCbs.length; i++) {
|
|
101
|
+
const cb = postFlushCbs[i];
|
|
102
|
+
postFlushCbsSet.delete(cb);
|
|
103
|
+
try {
|
|
104
|
+
cb();
|
|
105
|
+
} catch (e) {
|
|
106
|
+
if (globalErrorHandler) globalErrorHandler(e, "post-flush callback");
|
|
107
|
+
if (__DEV__) {
|
|
108
|
+
console.error("[LytJS] post-flush callback failed:", e);
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
postFlushCbs.length = 0;
|
|
113
|
+
}
|
|
114
|
+
if (iterations >= maxIterations) {
|
|
115
|
+
const remainingJobs = queue.length + preFlushCbs.length + postFlushCbs.length;
|
|
116
|
+
const msg = `[LytJS] flushJobs exceeded ${maxIterations} iterations. Possible infinite update loop detected.` + (remainingJobs > 0 ? ` ${remainingJobs} job(s) were discarded.` : "");
|
|
117
|
+
if (__DEV__) {
|
|
118
|
+
console.warn(msg);
|
|
119
|
+
} else {
|
|
120
|
+
console.error(msg);
|
|
121
|
+
}
|
|
122
|
+
queue.length = 0;
|
|
123
|
+
queueSet.clear();
|
|
124
|
+
preFlushCbs.length = 0;
|
|
125
|
+
preFlushCbsSet.clear();
|
|
126
|
+
postFlushCbs.length = 0;
|
|
127
|
+
postFlushCbsSet.clear();
|
|
128
|
+
}
|
|
129
|
+
} finally {
|
|
130
|
+
isFlushing = false;
|
|
131
|
+
if (preFlushCbs.length || queue.length || postFlushCbs.length) {
|
|
132
|
+
isFlushPending = true;
|
|
133
|
+
nextTick(flushJobs);
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
function flushQueueByPriority() {
|
|
138
|
+
const HIGH_THRESHOLD = Priority.NORMAL;
|
|
139
|
+
const executeBatch = (minPriority, maxPriority) => {
|
|
140
|
+
let i = 0;
|
|
141
|
+
while (i < queue.length) {
|
|
142
|
+
const job = queue[i];
|
|
143
|
+
const priority = job?.priority ?? Priority.NORMAL;
|
|
144
|
+
if (priority >= minPriority && priority <= maxPriority) {
|
|
145
|
+
queue.splice(i, 1);
|
|
146
|
+
queueSet.delete(job);
|
|
147
|
+
try {
|
|
148
|
+
job();
|
|
149
|
+
} catch (e) {
|
|
150
|
+
if (globalErrorHandler) globalErrorHandler(e, "job execution");
|
|
151
|
+
if (__DEV__) {
|
|
152
|
+
console.error("[LytJS] job execution failed:", e);
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
} else {
|
|
156
|
+
i++;
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
};
|
|
160
|
+
executeBatch(Priority.CRITICAL, HIGH_THRESHOLD);
|
|
161
|
+
executeBatch(Priority.NORMAL, Priority.LOW - 1);
|
|
162
|
+
executeBatch(Priority.LOW, Priority.IDLE);
|
|
163
|
+
}
|
|
164
|
+
function flushSync() {
|
|
165
|
+
if (isFlushing) return;
|
|
166
|
+
flushJobs();
|
|
167
|
+
}
|
|
168
|
+
function hasPendingJobs() {
|
|
169
|
+
return preFlushCbs.length > 0 || queue.length > 0 || postFlushCbs.length > 0;
|
|
170
|
+
}
|
|
171
|
+
function getPendingJobCount() {
|
|
172
|
+
return queue.length;
|
|
173
|
+
}
|
|
174
|
+
function queueFlush() {
|
|
175
|
+
if (!isFlushing && !isFlushPending) {
|
|
176
|
+
isFlushPending = true;
|
|
177
|
+
nextTick(flushJobs);
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
function resetSchedulerState() {
|
|
181
|
+
queue.length = 0;
|
|
182
|
+
queueSet.clear();
|
|
183
|
+
preFlushCbs.length = 0;
|
|
184
|
+
preFlushCbsSet.clear();
|
|
185
|
+
postFlushCbs.length = 0;
|
|
186
|
+
postFlushCbsSet.clear();
|
|
187
|
+
isFlushing = false;
|
|
188
|
+
isFlushPending = false;
|
|
189
|
+
maxIterations = DEFAULT_MAX_ITERATIONS;
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
export { Priority, flushJobs, flushSync, getPendingJobCount, hasPendingJobs, nextTick, queueJob, queueJobWithPriority, queuePostFlushCb, queuePreFlushCb, resetSchedulerState, setErrorHandler, setMaxIterations };
|
|
193
|
+
//# sourceMappingURL=index.mjs.map
|
|
194
|
+
//# sourceMappingURL=index.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/index.ts"],"names":[],"mappings":"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","file":"index.mjs","sourcesContent":["/**\r\n * @lytjs/common-scheduler\r\n * 任务调度器 - 管理异步任务队列\r\n * 支持 pre-flush 和 post-flush 两种队列\r\n */\r\n\r\n// ============================================================\r\n// 调度器类型\r\n// ============================================================\r\n\r\n/**\r\n * 调度器任务类型\r\n */\r\nexport type SchedulerJob = () => void;\r\n\r\n/**\r\n * 带优先级的调度器任务类型\r\n */\r\nexport interface SchedulerJobWithPriority extends SchedulerJob {\r\n /** 优先级,数值越小优先级越高 */\r\n priority?: number;\r\n}\r\n\r\n/**\r\n * 优先级常量\r\n */\r\nexport const Priority = {\r\n IDLE: 1000, // 空闲任务\r\n LOW: 500, // 低优先级\r\n NORMAL: 0, // 普通优先级(默认)\r\n HIGH: -500, // 高优先级\r\n CRITICAL: -1000, // 关键任务\r\n} as const;\r\n\r\n// ============================================================\r\n// 全局错误处理\r\n// ============================================================\r\n\r\nlet globalErrorHandler: ((error: Error, info: string) => void) | null = null;\r\nexport function setErrorHandler(handler: ((error: Error, info: string) => void) | null): void {\r\n globalErrorHandler = handler;\r\n}\r\n\r\n// ============================================================\r\n// 调度器状态\r\n// ============================================================\r\n\r\nlet isFlushing = false;\r\nlet isFlushPending = false;\r\n\r\n/** Default maximum iterations for flushJobs loop to prevent infinite update loops. */\r\nconst DEFAULT_MAX_ITERATIONS = 100;\r\n\r\nlet maxIterations = DEFAULT_MAX_ITERATIONS;\r\n\r\n/**\r\n * 设置 flushJobs 的最大迭代次数\r\n * 用于自定义调度器在复杂场景下的循环上限\r\n */\r\nexport function setMaxIterations(n: number): void {\r\n if (typeof n === 'number' && n > 0) {\r\n maxIterations = n;\r\n }\r\n}\r\n\r\nconst queue: SchedulerJob[] = [];\r\nconst queueSet: Set<SchedulerJob> = new Set();\r\nconst preFlushCbs: SchedulerJob[] = [];\r\nconst preFlushCbsSet: Set<SchedulerJob> = new Set();\r\nconst postFlushCbs: SchedulerJob[] = [];\r\nconst postFlushCbsSet: Set<SchedulerJob> = new Set();\r\n\r\nlet resolvedPromise: Promise<void> | null = null;\r\n\r\n/**\r\n * 获取当前环境的 resolvedPromise\r\n */\r\nfunction getResolvedPromise(): Promise<void> {\r\n return (resolvedPromise ??= Promise.resolve());\r\n}\r\n\r\n// ============================================================\r\n// 公共 API\r\n// ============================================================\r\n\r\n/**\r\n * 将任务加入队列\r\n * 同一个任务(引用相同)只会被加入一次\r\n */\r\nexport function queueJob(job: SchedulerJob): void {\r\n if (!queueSet.has(job)) {\r\n queueSet.add(job);\r\n queue.push(job);\r\n queueFlush();\r\n }\r\n}\r\n\r\n/**\r\n * 将带优先级的任务加入队列\r\n * 按优先级插入(优先级高的在前),相同优先级保持插入顺序(稳定排序)\r\n */\r\nexport function queueJobWithPriority(job: SchedulerJobWithPriority): void {\r\n if (!queueSet.has(job)) {\r\n queueSet.add(job);\r\n const priority = job.priority ?? Priority.NORMAL;\r\n // 找到第一个优先级大于等于当前 job 的位置(即当前 job 应该插入的位置)\r\n // 这样优先级高的(数值小的)排在前面,相同优先级的保持插入顺序\r\n let insertIndex = queue.length;\r\n for (let i = 0; i < queue.length; i++) {\r\n const existingJob = queue[i] as SchedulerJobWithPriority | undefined;\r\n const existingPriority = existingJob?.priority ?? Priority.NORMAL;\r\n if (existingPriority > priority) {\r\n insertIndex = i;\r\n break;\r\n }\r\n }\r\n queue.splice(insertIndex, 0, job);\r\n queueFlush();\r\n }\r\n}\r\n\r\n/**\r\n * 将回调加入 pre-flush 队列\r\n * 在主 job 队列之前执行(用于 watch 的 \"pre\" 模式)\r\n */\r\nexport function queuePreFlushCb(cb: SchedulerJob): void {\r\n if (!preFlushCbsSet.has(cb)) {\r\n preFlushCbsSet.add(cb);\r\n preFlushCbs.push(cb);\r\n queueFlush();\r\n }\r\n}\r\n\r\n/**\r\n * 将回调加入 post-flush 队列\r\n * 在所有 job 执行完毕后执行\r\n */\r\nexport function queuePostFlushCb(cb: SchedulerJob): void {\r\n if (!postFlushCbsSet.has(cb)) {\r\n postFlushCbsSet.add(cb);\r\n postFlushCbs.push(cb);\r\n queueFlush();\r\n }\r\n}\r\n\r\n/**\r\n * 在下一个 tick 执行回调\r\n */\r\nexport function nextTick(cb?: SchedulerJob): Promise<void> {\r\n const p = getResolvedPromise();\r\n return cb ? p.then(cb) : p;\r\n}\r\n\r\n/**\r\n * 刷新所有待执行的任务\r\n * 执行顺序:pre-flush 回调 -> 主 job 队列(按优先级) -> post-flush 回调\r\n * 支持循环处理:执行期间新增的回调也会被处理\r\n * 优先级策略:每轮先执行 CRITICAL/HIGH,再 NORMAL,最后 LOW/IDLE\r\n */\r\nexport function flushJobs(): void {\r\n isFlushing = true;\r\n isFlushPending = false;\r\n\r\n let iterations = 0;\r\n\r\n try {\r\n // 循环处理:执行期间新增的回调也会被处理\r\n while (\r\n (preFlushCbs.length > 0 || queue.length > 0 || postFlushCbs.length > 0) &&\r\n iterations < maxIterations\r\n ) {\r\n iterations++;\r\n\r\n // 1. 执行 pre-flush 队列(watch pre 模式回调)\r\n for (let i = 0; i < preFlushCbs.length; i++) {\r\n const cb = preFlushCbs[i]!;\r\n preFlushCbsSet.delete(cb);\r\n try {\r\n cb();\r\n } catch (e) {\r\n if (globalErrorHandler) globalErrorHandler(e as Error, 'pre-flush callback');\r\n if (__DEV__) {\r\n console.error('[LytJS] pre-flush callback failed:', e);\r\n }\r\n }\r\n }\r\n preFlushCbs.length = 0;\r\n\r\n // 2. 执行主 job 队列(按优先级分组执行)\r\n flushQueueByPriority();\r\n\r\n // 3. 执行 post-flush 回调\r\n for (let i = 0; i < postFlushCbs.length; i++) {\r\n const cb = postFlushCbs[i]!;\r\n postFlushCbsSet.delete(cb);\r\n try {\r\n cb();\r\n } catch (e) {\r\n if (globalErrorHandler) globalErrorHandler(e as Error, 'post-flush callback');\r\n if (__DEV__) {\r\n console.error('[LytJS] post-flush callback failed:', e);\r\n }\r\n }\r\n }\r\n postFlushCbs.length = 0;\r\n }\r\n\r\n if (iterations >= maxIterations) {\r\n const remainingJobs = queue.length + preFlushCbs.length + postFlushCbs.length;\r\n const msg =\r\n `[LytJS] flushJobs exceeded ${maxIterations} iterations. ` +\r\n `Possible infinite update loop detected.` +\r\n (remainingJobs > 0 ? ` ${remainingJobs} job(s) were discarded.` : '');\r\n if (__DEV__) {\r\n console.warn(msg);\r\n } else {\r\n console.error(msg);\r\n }\r\n // Clear all queues to prevent re-triggering\r\n queue.length = 0;\r\n queueSet.clear();\r\n preFlushCbs.length = 0;\r\n preFlushCbsSet.clear();\r\n postFlushCbs.length = 0;\r\n postFlushCbsSet.clear();\r\n }\r\n } finally {\r\n isFlushing = false;\r\n\r\n // If new jobs were queued during flush, schedule next flush via nextTick\r\n // instead of recursive call to avoid stack overflow\r\n if (preFlushCbs.length || queue.length || postFlushCbs.length) {\r\n isFlushPending = true;\r\n nextTick(flushJobs);\r\n }\r\n }\r\n}\r\n\r\n/**\r\n * 按优先级分组执行队列中的任务\r\n * 执行顺序:CRITICAL/HIGH -> NORMAL -> LOW/IDLE\r\n * 如果某轮执行中新增了高优先级任务,优先执行它们\r\n */\r\nfunction flushQueueByPriority(): void {\r\n // 优先级阈值定义\r\n const HIGH_THRESHOLD = Priority.NORMAL; // <= 0 视为高优先级(CRITICAL: -1000, HIGH: -500)\r\n\r\n // 分组执行,每执行一组后检查是否有新的高优先级任务加入\r\n const executeBatch = (minPriority: number, maxPriority: number): void => {\r\n let i = 0;\r\n while (i < queue.length) {\r\n const job = queue[i] as SchedulerJobWithPriority | undefined;\r\n const priority = job?.priority ?? Priority.NORMAL;\r\n if (priority >= minPriority && priority <= maxPriority) {\r\n // 移除并执行\r\n queue.splice(i, 1);\r\n queueSet.delete(job!);\r\n try {\r\n job!();\r\n } catch (e) {\r\n if (globalErrorHandler) globalErrorHandler(e as Error, 'job execution');\r\n if (__DEV__) {\r\n console.error('[LytJS] job execution failed:', e);\r\n }\r\n }\r\n // 不递增 i,因为 splice 后下一个元素移到了当前位置\r\n } else {\r\n i++;\r\n }\r\n }\r\n };\r\n\r\n // 第一轮:执行 CRITICAL 和 HIGH 任务(priority <= 0)\r\n executeBatch(Priority.CRITICAL, HIGH_THRESHOLD);\r\n\r\n // 第二轮:执行 NORMAL 任务(priority >= 0 && priority < 500)\r\n executeBatch(Priority.NORMAL, Priority.LOW - 1);\r\n\r\n // 第三轮:执行 LOW 和 IDLE 任务(priority >= 500)\r\n executeBatch(Priority.LOW, Priority.IDLE);\r\n}\r\n\r\n/**\r\n * 同步刷新所有任务\r\n */\r\nexport function flushSync(): void {\r\n if (isFlushing) return;\r\n flushJobs();\r\n}\r\n\r\n/**\r\n * 检查是否有待执行的任务\r\n */\r\nexport function hasPendingJobs(): boolean {\r\n return preFlushCbs.length > 0 || queue.length > 0 || postFlushCbs.length > 0;\r\n}\r\n\r\n/**\r\n * 获取待执行的 job 数量\r\n */\r\nexport function getPendingJobCount(): number {\r\n return queue.length;\r\n}\r\n\r\n/**\r\n * 调度一次 flush\r\n */\r\nfunction queueFlush(): void {\r\n if (!isFlushing && !isFlushPending) {\r\n isFlushPending = true;\r\n nextTick(flushJobs);\r\n }\r\n}\r\n\r\n/**\r\n * 重置调度器状态(用于测试)\r\n */\r\nexport function resetSchedulerState(): void {\r\n queue.length = 0;\r\n queueSet.clear();\r\n preFlushCbs.length = 0;\r\n preFlushCbsSet.clear();\r\n postFlushCbs.length = 0;\r\n postFlushCbsSet.clear();\r\n isFlushing = false;\r\n isFlushPending = false;\r\n maxIterations = DEFAULT_MAX_ITERATIONS;\r\n}\r\n"]}
|
package/package.json
ADDED
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@lytjs/common-scheduler",
|
|
3
|
+
"version": "6.0.0",
|
|
4
|
+
"description": "Task scheduler for LytJS",
|
|
5
|
+
"main": "./dist/index.cjs",
|
|
6
|
+
"module": "./dist/index.mjs",
|
|
7
|
+
"types": "./dist/index.d.ts",
|
|
8
|
+
"exports": {
|
|
9
|
+
".": {
|
|
10
|
+
"types": "./dist/index.d.ts",
|
|
11
|
+
"import": "./dist/index.mjs",
|
|
12
|
+
"require": "./dist/index.cjs"
|
|
13
|
+
}
|
|
14
|
+
},
|
|
15
|
+
"files": [
|
|
16
|
+
"dist",
|
|
17
|
+
"src"
|
|
18
|
+
],
|
|
19
|
+
"scripts": {
|
|
20
|
+
"test": "vitest run",
|
|
21
|
+
"test:watch": "vitest",
|
|
22
|
+
"test:coverage": "vitest run --coverage",
|
|
23
|
+
"lint": "eslint \"src/**/*.ts\"",
|
|
24
|
+
"build": "tsup",
|
|
25
|
+
"type-check": "tsc --noEmit",
|
|
26
|
+
"clean": "rm -rf dist"
|
|
27
|
+
},
|
|
28
|
+
"sideEffects": false,
|
|
29
|
+
"license": "MIT",
|
|
30
|
+
"dependencies": {
|
|
31
|
+
"@lytjs/common-env": "^6.0.0"
|
|
32
|
+
},
|
|
33
|
+
"devDependencies": {
|
|
34
|
+
"tsup": "^8.4.0",
|
|
35
|
+
"typescript": "^5.8.2",
|
|
36
|
+
"vitest": "^3.0.7"
|
|
37
|
+
}
|
|
38
|
+
}
|
package/src/env.d.ts
ADDED
package/src/index.ts
ADDED
|
@@ -0,0 +1,328 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @lytjs/common-scheduler
|
|
3
|
+
* 任务调度器 - 管理异步任务队列
|
|
4
|
+
* 支持 pre-flush 和 post-flush 两种队列
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
// ============================================================
|
|
8
|
+
// 调度器类型
|
|
9
|
+
// ============================================================
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* 调度器任务类型
|
|
13
|
+
*/
|
|
14
|
+
export type SchedulerJob = () => void;
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* 带优先级的调度器任务类型
|
|
18
|
+
*/
|
|
19
|
+
export interface SchedulerJobWithPriority extends SchedulerJob {
|
|
20
|
+
/** 优先级,数值越小优先级越高 */
|
|
21
|
+
priority?: number;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* 优先级常量
|
|
26
|
+
*/
|
|
27
|
+
export const Priority = {
|
|
28
|
+
IDLE: 1000, // 空闲任务
|
|
29
|
+
LOW: 500, // 低优先级
|
|
30
|
+
NORMAL: 0, // 普通优先级(默认)
|
|
31
|
+
HIGH: -500, // 高优先级
|
|
32
|
+
CRITICAL: -1000, // 关键任务
|
|
33
|
+
} as const;
|
|
34
|
+
|
|
35
|
+
// ============================================================
|
|
36
|
+
// 全局错误处理
|
|
37
|
+
// ============================================================
|
|
38
|
+
|
|
39
|
+
let globalErrorHandler: ((error: Error, info: string) => void) | null = null;
|
|
40
|
+
export function setErrorHandler(handler: ((error: Error, info: string) => void) | null): void {
|
|
41
|
+
globalErrorHandler = handler;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
// ============================================================
|
|
45
|
+
// 调度器状态
|
|
46
|
+
// ============================================================
|
|
47
|
+
|
|
48
|
+
let isFlushing = false;
|
|
49
|
+
let isFlushPending = false;
|
|
50
|
+
|
|
51
|
+
/** Default maximum iterations for flushJobs loop to prevent infinite update loops. */
|
|
52
|
+
const DEFAULT_MAX_ITERATIONS = 100;
|
|
53
|
+
|
|
54
|
+
let maxIterations = DEFAULT_MAX_ITERATIONS;
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* 设置 flushJobs 的最大迭代次数
|
|
58
|
+
* 用于自定义调度器在复杂场景下的循环上限
|
|
59
|
+
*/
|
|
60
|
+
export function setMaxIterations(n: number): void {
|
|
61
|
+
if (typeof n === 'number' && n > 0) {
|
|
62
|
+
maxIterations = n;
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
const queue: SchedulerJob[] = [];
|
|
67
|
+
const queueSet: Set<SchedulerJob> = new Set();
|
|
68
|
+
const preFlushCbs: SchedulerJob[] = [];
|
|
69
|
+
const preFlushCbsSet: Set<SchedulerJob> = new Set();
|
|
70
|
+
const postFlushCbs: SchedulerJob[] = [];
|
|
71
|
+
const postFlushCbsSet: Set<SchedulerJob> = new Set();
|
|
72
|
+
|
|
73
|
+
let resolvedPromise: Promise<void> | null = null;
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* 获取当前环境的 resolvedPromise
|
|
77
|
+
*/
|
|
78
|
+
function getResolvedPromise(): Promise<void> {
|
|
79
|
+
return (resolvedPromise ??= Promise.resolve());
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
// ============================================================
|
|
83
|
+
// 公共 API
|
|
84
|
+
// ============================================================
|
|
85
|
+
|
|
86
|
+
/**
|
|
87
|
+
* 将任务加入队列
|
|
88
|
+
* 同一个任务(引用相同)只会被加入一次
|
|
89
|
+
*/
|
|
90
|
+
export function queueJob(job: SchedulerJob): void {
|
|
91
|
+
if (!queueSet.has(job)) {
|
|
92
|
+
queueSet.add(job);
|
|
93
|
+
queue.push(job);
|
|
94
|
+
queueFlush();
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
/**
|
|
99
|
+
* 将带优先级的任务加入队列
|
|
100
|
+
* 按优先级插入(优先级高的在前),相同优先级保持插入顺序(稳定排序)
|
|
101
|
+
*/
|
|
102
|
+
export function queueJobWithPriority(job: SchedulerJobWithPriority): void {
|
|
103
|
+
if (!queueSet.has(job)) {
|
|
104
|
+
queueSet.add(job);
|
|
105
|
+
const priority = job.priority ?? Priority.NORMAL;
|
|
106
|
+
// 找到第一个优先级大于等于当前 job 的位置(即当前 job 应该插入的位置)
|
|
107
|
+
// 这样优先级高的(数值小的)排在前面,相同优先级的保持插入顺序
|
|
108
|
+
let insertIndex = queue.length;
|
|
109
|
+
for (let i = 0; i < queue.length; i++) {
|
|
110
|
+
const existingJob = queue[i] as SchedulerJobWithPriority | undefined;
|
|
111
|
+
const existingPriority = existingJob?.priority ?? Priority.NORMAL;
|
|
112
|
+
if (existingPriority > priority) {
|
|
113
|
+
insertIndex = i;
|
|
114
|
+
break;
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
queue.splice(insertIndex, 0, job);
|
|
118
|
+
queueFlush();
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
/**
|
|
123
|
+
* 将回调加入 pre-flush 队列
|
|
124
|
+
* 在主 job 队列之前执行(用于 watch 的 "pre" 模式)
|
|
125
|
+
*/
|
|
126
|
+
export function queuePreFlushCb(cb: SchedulerJob): void {
|
|
127
|
+
if (!preFlushCbsSet.has(cb)) {
|
|
128
|
+
preFlushCbsSet.add(cb);
|
|
129
|
+
preFlushCbs.push(cb);
|
|
130
|
+
queueFlush();
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
/**
|
|
135
|
+
* 将回调加入 post-flush 队列
|
|
136
|
+
* 在所有 job 执行完毕后执行
|
|
137
|
+
*/
|
|
138
|
+
export function queuePostFlushCb(cb: SchedulerJob): void {
|
|
139
|
+
if (!postFlushCbsSet.has(cb)) {
|
|
140
|
+
postFlushCbsSet.add(cb);
|
|
141
|
+
postFlushCbs.push(cb);
|
|
142
|
+
queueFlush();
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
/**
|
|
147
|
+
* 在下一个 tick 执行回调
|
|
148
|
+
*/
|
|
149
|
+
export function nextTick(cb?: SchedulerJob): Promise<void> {
|
|
150
|
+
const p = getResolvedPromise();
|
|
151
|
+
return cb ? p.then(cb) : p;
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
/**
|
|
155
|
+
* 刷新所有待执行的任务
|
|
156
|
+
* 执行顺序:pre-flush 回调 -> 主 job 队列(按优先级) -> post-flush 回调
|
|
157
|
+
* 支持循环处理:执行期间新增的回调也会被处理
|
|
158
|
+
* 优先级策略:每轮先执行 CRITICAL/HIGH,再 NORMAL,最后 LOW/IDLE
|
|
159
|
+
*/
|
|
160
|
+
export function flushJobs(): void {
|
|
161
|
+
isFlushing = true;
|
|
162
|
+
isFlushPending = false;
|
|
163
|
+
|
|
164
|
+
let iterations = 0;
|
|
165
|
+
|
|
166
|
+
try {
|
|
167
|
+
// 循环处理:执行期间新增的回调也会被处理
|
|
168
|
+
while (
|
|
169
|
+
(preFlushCbs.length > 0 || queue.length > 0 || postFlushCbs.length > 0) &&
|
|
170
|
+
iterations < maxIterations
|
|
171
|
+
) {
|
|
172
|
+
iterations++;
|
|
173
|
+
|
|
174
|
+
// 1. 执行 pre-flush 队列(watch pre 模式回调)
|
|
175
|
+
for (let i = 0; i < preFlushCbs.length; i++) {
|
|
176
|
+
const cb = preFlushCbs[i]!;
|
|
177
|
+
preFlushCbsSet.delete(cb);
|
|
178
|
+
try {
|
|
179
|
+
cb();
|
|
180
|
+
} catch (e) {
|
|
181
|
+
if (globalErrorHandler) globalErrorHandler(e as Error, 'pre-flush callback');
|
|
182
|
+
if (__DEV__) {
|
|
183
|
+
console.error('[LytJS] pre-flush callback failed:', e);
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
preFlushCbs.length = 0;
|
|
188
|
+
|
|
189
|
+
// 2. 执行主 job 队列(按优先级分组执行)
|
|
190
|
+
flushQueueByPriority();
|
|
191
|
+
|
|
192
|
+
// 3. 执行 post-flush 回调
|
|
193
|
+
for (let i = 0; i < postFlushCbs.length; i++) {
|
|
194
|
+
const cb = postFlushCbs[i]!;
|
|
195
|
+
postFlushCbsSet.delete(cb);
|
|
196
|
+
try {
|
|
197
|
+
cb();
|
|
198
|
+
} catch (e) {
|
|
199
|
+
if (globalErrorHandler) globalErrorHandler(e as Error, 'post-flush callback');
|
|
200
|
+
if (__DEV__) {
|
|
201
|
+
console.error('[LytJS] post-flush callback failed:', e);
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
postFlushCbs.length = 0;
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
if (iterations >= maxIterations) {
|
|
209
|
+
const remainingJobs = queue.length + preFlushCbs.length + postFlushCbs.length;
|
|
210
|
+
const msg =
|
|
211
|
+
`[LytJS] flushJobs exceeded ${maxIterations} iterations. ` +
|
|
212
|
+
`Possible infinite update loop detected.` +
|
|
213
|
+
(remainingJobs > 0 ? ` ${remainingJobs} job(s) were discarded.` : '');
|
|
214
|
+
if (__DEV__) {
|
|
215
|
+
console.warn(msg);
|
|
216
|
+
} else {
|
|
217
|
+
console.error(msg);
|
|
218
|
+
}
|
|
219
|
+
// Clear all queues to prevent re-triggering
|
|
220
|
+
queue.length = 0;
|
|
221
|
+
queueSet.clear();
|
|
222
|
+
preFlushCbs.length = 0;
|
|
223
|
+
preFlushCbsSet.clear();
|
|
224
|
+
postFlushCbs.length = 0;
|
|
225
|
+
postFlushCbsSet.clear();
|
|
226
|
+
}
|
|
227
|
+
} finally {
|
|
228
|
+
isFlushing = false;
|
|
229
|
+
|
|
230
|
+
// If new jobs were queued during flush, schedule next flush via nextTick
|
|
231
|
+
// instead of recursive call to avoid stack overflow
|
|
232
|
+
if (preFlushCbs.length || queue.length || postFlushCbs.length) {
|
|
233
|
+
isFlushPending = true;
|
|
234
|
+
nextTick(flushJobs);
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
/**
|
|
240
|
+
* 按优先级分组执行队列中的任务
|
|
241
|
+
* 执行顺序:CRITICAL/HIGH -> NORMAL -> LOW/IDLE
|
|
242
|
+
* 如果某轮执行中新增了高优先级任务,优先执行它们
|
|
243
|
+
*/
|
|
244
|
+
function flushQueueByPriority(): void {
|
|
245
|
+
// 优先级阈值定义
|
|
246
|
+
const HIGH_THRESHOLD = Priority.NORMAL; // <= 0 视为高优先级(CRITICAL: -1000, HIGH: -500)
|
|
247
|
+
|
|
248
|
+
// 分组执行,每执行一组后检查是否有新的高优先级任务加入
|
|
249
|
+
const executeBatch = (minPriority: number, maxPriority: number): void => {
|
|
250
|
+
let i = 0;
|
|
251
|
+
while (i < queue.length) {
|
|
252
|
+
const job = queue[i] as SchedulerJobWithPriority | undefined;
|
|
253
|
+
const priority = job?.priority ?? Priority.NORMAL;
|
|
254
|
+
if (priority >= minPriority && priority <= maxPriority) {
|
|
255
|
+
// 移除并执行
|
|
256
|
+
queue.splice(i, 1);
|
|
257
|
+
queueSet.delete(job!);
|
|
258
|
+
try {
|
|
259
|
+
job!();
|
|
260
|
+
} catch (e) {
|
|
261
|
+
if (globalErrorHandler) globalErrorHandler(e as Error, 'job execution');
|
|
262
|
+
if (__DEV__) {
|
|
263
|
+
console.error('[LytJS] job execution failed:', e);
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
// 不递增 i,因为 splice 后下一个元素移到了当前位置
|
|
267
|
+
} else {
|
|
268
|
+
i++;
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
};
|
|
272
|
+
|
|
273
|
+
// 第一轮:执行 CRITICAL 和 HIGH 任务(priority <= 0)
|
|
274
|
+
executeBatch(Priority.CRITICAL, HIGH_THRESHOLD);
|
|
275
|
+
|
|
276
|
+
// 第二轮:执行 NORMAL 任务(priority >= 0 && priority < 500)
|
|
277
|
+
executeBatch(Priority.NORMAL, Priority.LOW - 1);
|
|
278
|
+
|
|
279
|
+
// 第三轮:执行 LOW 和 IDLE 任务(priority >= 500)
|
|
280
|
+
executeBatch(Priority.LOW, Priority.IDLE);
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
/**
|
|
284
|
+
* 同步刷新所有任务
|
|
285
|
+
*/
|
|
286
|
+
export function flushSync(): void {
|
|
287
|
+
if (isFlushing) return;
|
|
288
|
+
flushJobs();
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
/**
|
|
292
|
+
* 检查是否有待执行的任务
|
|
293
|
+
*/
|
|
294
|
+
export function hasPendingJobs(): boolean {
|
|
295
|
+
return preFlushCbs.length > 0 || queue.length > 0 || postFlushCbs.length > 0;
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
/**
|
|
299
|
+
* 获取待执行的 job 数量
|
|
300
|
+
*/
|
|
301
|
+
export function getPendingJobCount(): number {
|
|
302
|
+
return queue.length;
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
/**
|
|
306
|
+
* 调度一次 flush
|
|
307
|
+
*/
|
|
308
|
+
function queueFlush(): void {
|
|
309
|
+
if (!isFlushing && !isFlushPending) {
|
|
310
|
+
isFlushPending = true;
|
|
311
|
+
nextTick(flushJobs);
|
|
312
|
+
}
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
/**
|
|
316
|
+
* 重置调度器状态(用于测试)
|
|
317
|
+
*/
|
|
318
|
+
export function resetSchedulerState(): void {
|
|
319
|
+
queue.length = 0;
|
|
320
|
+
queueSet.clear();
|
|
321
|
+
preFlushCbs.length = 0;
|
|
322
|
+
preFlushCbsSet.clear();
|
|
323
|
+
postFlushCbs.length = 0;
|
|
324
|
+
postFlushCbsSet.clear();
|
|
325
|
+
isFlushing = false;
|
|
326
|
+
isFlushPending = false;
|
|
327
|
+
maxIterations = DEFAULT_MAX_ITERATIONS;
|
|
328
|
+
}
|