aoye 0.0.1 → 0.0.2
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/README.md +40 -76
- package/dist/aoye.cjs.js +854 -696
- package/dist/aoye.esm.js +854 -697
- package/dist/aoye.umd.js +491 -331
- package/dist/shared/event.d.ts +72 -0
- package/dist/signal/src/global.d.ts +4 -0
- package/dist/signal/src/index.d.ts +1 -0
- package/dist/signal/src/schedule.d.ts +1 -1
- package/dist/signal/src/scope.d.ts +3 -1
- package/dist/signal/src/util.d.ts +2 -0
- package/package.json +2 -2
package/dist/aoye.umd.js
CHANGED
|
@@ -4,47 +4,6 @@
|
|
|
4
4
|
(global = typeof globalThis !== 'undefined' ? globalThis : global || self, factory(global.Aoye = {}));
|
|
5
5
|
})(this, (function (exports) { 'use strict';
|
|
6
6
|
|
|
7
|
-
class Queue {
|
|
8
|
-
constructor() {
|
|
9
|
-
this.len = 0;
|
|
10
|
-
}
|
|
11
|
-
get first() {
|
|
12
|
-
var _a;
|
|
13
|
-
return (_a = this._first) === null || _a === void 0 ? void 0 : _a.v;
|
|
14
|
-
}
|
|
15
|
-
get last() {
|
|
16
|
-
var _a;
|
|
17
|
-
return (_a = this._last) === null || _a === void 0 ? void 0 : _a.v;
|
|
18
|
-
}
|
|
19
|
-
push(it) {
|
|
20
|
-
this.len++;
|
|
21
|
-
const { _last: last } = this;
|
|
22
|
-
const item = { v: it };
|
|
23
|
-
if (!last) {
|
|
24
|
-
this._first = this._last = item;
|
|
25
|
-
return;
|
|
26
|
-
}
|
|
27
|
-
item.prev = this._last;
|
|
28
|
-
last.next = item;
|
|
29
|
-
this._last = item;
|
|
30
|
-
}
|
|
31
|
-
shift() {
|
|
32
|
-
const { _first: first } = this;
|
|
33
|
-
if (!first)
|
|
34
|
-
return undefined;
|
|
35
|
-
this.len--;
|
|
36
|
-
const { next } = first;
|
|
37
|
-
first.next = undefined;
|
|
38
|
-
if (next) {
|
|
39
|
-
next.prev = undefined;
|
|
40
|
-
}
|
|
41
|
-
else {
|
|
42
|
-
this._last = undefined;
|
|
43
|
-
}
|
|
44
|
-
this._first = next;
|
|
45
|
-
return first.v;
|
|
46
|
-
}
|
|
47
|
-
}
|
|
48
7
|
class SortMap {
|
|
49
8
|
constructor() {
|
|
50
9
|
this.data = {};
|
|
@@ -78,6 +37,151 @@
|
|
|
78
37
|
// queue.push(10)
|
|
79
38
|
// queue.array();
|
|
80
39
|
|
|
40
|
+
const timestamp = globalThis.performance ? globalThis.performance.now.bind(globalThis.performance) : Date.now;
|
|
41
|
+
var EventMode;
|
|
42
|
+
(function (EventMode) {
|
|
43
|
+
EventMode[EventMode["Immediate"] = 0] = "Immediate";
|
|
44
|
+
EventMode[EventMode["Queue"] = 1] = "Queue";
|
|
45
|
+
})(EventMode || (EventMode = {}));
|
|
46
|
+
var ProcessStatus;
|
|
47
|
+
(function (ProcessStatus) {
|
|
48
|
+
ProcessStatus[ProcessStatus["None"] = 0] = "None";
|
|
49
|
+
ProcessStatus[ProcessStatus["Processing"] = 1] = "Processing";
|
|
50
|
+
ProcessStatus[ProcessStatus["Paused"] = 2] = "Paused";
|
|
51
|
+
})(ProcessStatus || (ProcessStatus = {}));
|
|
52
|
+
const DefaultEventOpt = {
|
|
53
|
+
mode: EventMode.Immediate,
|
|
54
|
+
};
|
|
55
|
+
const ALL = '__ALL_KEY';
|
|
56
|
+
class BaseEvent {
|
|
57
|
+
constructor(opt = {}) {
|
|
58
|
+
this.opt = opt;
|
|
59
|
+
this.eventQueue = [];
|
|
60
|
+
this.status = ProcessStatus.None;
|
|
61
|
+
this.subMap = new Map();
|
|
62
|
+
this.on = (type, fn) => {
|
|
63
|
+
if (type == null)
|
|
64
|
+
type = ALL;
|
|
65
|
+
const suber = this.subMap.get(type) || new Set();
|
|
66
|
+
suber.add(fn);
|
|
67
|
+
this.subMap.set(type, suber);
|
|
68
|
+
};
|
|
69
|
+
this.off = (type, fn) => {
|
|
70
|
+
const suber = this.subMap.get(type !== null && type !== void 0 ? type : ALL);
|
|
71
|
+
if (!suber)
|
|
72
|
+
return;
|
|
73
|
+
suber.delete(fn);
|
|
74
|
+
};
|
|
75
|
+
this.once = (type, fn) => {
|
|
76
|
+
fn['once'] = true;
|
|
77
|
+
this.on(type, fn);
|
|
78
|
+
};
|
|
79
|
+
this.promiseOnce = (type) => {
|
|
80
|
+
return new Promise((resolve) => {
|
|
81
|
+
this.once(type, (...args) => {
|
|
82
|
+
resolve(args);
|
|
83
|
+
});
|
|
84
|
+
});
|
|
85
|
+
};
|
|
86
|
+
this.setScheduler = (type, scheduler) => {
|
|
87
|
+
if (typeof type !== 'string') {
|
|
88
|
+
this.scheduler = type;
|
|
89
|
+
return;
|
|
90
|
+
}
|
|
91
|
+
const set = this.subMap.get(type) || new Set();
|
|
92
|
+
set['scheduler'] = scheduler;
|
|
93
|
+
this.subMap.set(type, set);
|
|
94
|
+
};
|
|
95
|
+
// construct 会初始化为下面其中一种
|
|
96
|
+
this.emit = (type, ...args) => {
|
|
97
|
+
this.opt.mode === EventMode.Immediate
|
|
98
|
+
? this.emitImmediate(type, ...args)
|
|
99
|
+
: this.emitQueue(type, ...args);
|
|
100
|
+
};
|
|
101
|
+
this.pause = () => (this.status = ProcessStatus.Paused);
|
|
102
|
+
this.unPause = () => (this.status = ProcessStatus.None);
|
|
103
|
+
this.start = () => {
|
|
104
|
+
this.status = ProcessStatus.None;
|
|
105
|
+
this.processQueue();
|
|
106
|
+
};
|
|
107
|
+
this.process = () => {
|
|
108
|
+
if (this.scheduler) {
|
|
109
|
+
return this.scheduler(this.recallScheduler);
|
|
110
|
+
}
|
|
111
|
+
return this.processQueue();
|
|
112
|
+
};
|
|
113
|
+
this.recallScheduler = () => {
|
|
114
|
+
this.scheduler(this.recallScheduler);
|
|
115
|
+
};
|
|
116
|
+
this.processQueue = () => {
|
|
117
|
+
// 如果是挂起状态则直接结束
|
|
118
|
+
if (this.status === ProcessStatus.Paused)
|
|
119
|
+
return;
|
|
120
|
+
this.status = ProcessStatus.Processing;
|
|
121
|
+
let { type, args } = this.eventQueue.shift() || {};
|
|
122
|
+
if (type) {
|
|
123
|
+
// 在此过程中用户可通过 pause 和 start 同步控制事件处理
|
|
124
|
+
const fns = this.subMap.get(type);
|
|
125
|
+
const allSub = this.subMap.get(ALL);
|
|
126
|
+
fns === null || fns === void 0 ? void 0 : fns.forEach((it) => this.callSub(it, fns, args));
|
|
127
|
+
allSub === null || allSub === void 0 ? void 0 : allSub.forEach((it) => this.callSub(it, allSub, args));
|
|
128
|
+
if (this.eventQueue.length > 0) {
|
|
129
|
+
this.processQueue();
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
//@ts-ignore 队列全部处理完成,如果执行过程中被 pause
|
|
133
|
+
if (this.status !== ProcessStatus.Paused) {
|
|
134
|
+
this.status = ProcessStatus.None;
|
|
135
|
+
}
|
|
136
|
+
};
|
|
137
|
+
this.dispatchEvent = (iList) => {
|
|
138
|
+
// 从大到小排序
|
|
139
|
+
iList.sort((a, b) => b - a);
|
|
140
|
+
iList.forEach((idx) => {
|
|
141
|
+
const [item] = this.eventQueue.splice(idx, 1);
|
|
142
|
+
const { type, args } = item || {};
|
|
143
|
+
if (type && args) {
|
|
144
|
+
this.emitImmediate(type, ...args);
|
|
145
|
+
}
|
|
146
|
+
});
|
|
147
|
+
};
|
|
148
|
+
this.clear = () => {
|
|
149
|
+
this.subMap.clear();
|
|
150
|
+
this.eventQueue = [];
|
|
151
|
+
this.scheduler = undefined;
|
|
152
|
+
};
|
|
153
|
+
this.opt = { ...DefaultEventOpt, ...opt };
|
|
154
|
+
}
|
|
155
|
+
callSub(it, fns, args) {
|
|
156
|
+
const doCall = (...args) => {
|
|
157
|
+
it(...args);
|
|
158
|
+
if (it['once'] === true)
|
|
159
|
+
fns.delete(it);
|
|
160
|
+
};
|
|
161
|
+
const scheduler = it['scheduler'] || fns['scheduler'];
|
|
162
|
+
if (scheduler) {
|
|
163
|
+
scheduler(doCall, ...args);
|
|
164
|
+
}
|
|
165
|
+
else {
|
|
166
|
+
it(...args);
|
|
167
|
+
if (it['once'] === true)
|
|
168
|
+
fns.delete(it);
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
emitImmediate(type, ...args) {
|
|
172
|
+
const fns = this.subMap.get(type);
|
|
173
|
+
const allSub = this.subMap.get(ALL);
|
|
174
|
+
fns === null || fns === void 0 ? void 0 : fns.forEach((it) => this.callSub(it, fns, args));
|
|
175
|
+
allSub === null || allSub === void 0 ? void 0 : allSub.forEach((it) => this.callSub(it, allSub, args));
|
|
176
|
+
}
|
|
177
|
+
emitQueue(type, ...args) {
|
|
178
|
+
this.eventQueue.push({ type, args, time: timestamp() });
|
|
179
|
+
this.process();
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
BaseEvent.a = 19;
|
|
183
|
+
|
|
184
|
+
const evt = new BaseEvent();
|
|
81
185
|
const G = {
|
|
82
186
|
/** 原子 signal 更新次数 */
|
|
83
187
|
version: 0,
|
|
@@ -90,6 +194,7 @@
|
|
|
90
194
|
(function (State) {
|
|
91
195
|
State[State["Clean"] = 0] = "Clean";
|
|
92
196
|
/** 仅用于 scope 节点是否 abort */
|
|
197
|
+
State[State["ScopeAborted"] = 64] = "ScopeAborted";
|
|
93
198
|
State[State["ScopeAbort"] = 32] = "ScopeAbort";
|
|
94
199
|
State[State["OutLink"] = 16] = "OutLink";
|
|
95
200
|
State[State["Unknown"] = 8] = "Unknown";
|
|
@@ -98,6 +203,259 @@
|
|
|
98
203
|
State[State["ScopeReady"] = 1] = "ScopeReady";
|
|
99
204
|
})(State || (State = {}));
|
|
100
205
|
const DirtyState = State.Unknown | State.Dirty;
|
|
206
|
+
const ScopeExecuted = State.ScopeReady | State.ScopeAbort | State.ScopeAborted;
|
|
207
|
+
|
|
208
|
+
/**
|
|
209
|
+
* 这是一个优先队列 (满足子节点总是比父节点大)
|
|
210
|
+
* 1
|
|
211
|
+
*
|
|
212
|
+
* 10 20
|
|
213
|
+
*
|
|
214
|
+
* 15 30 25 30
|
|
215
|
+
*
|
|
216
|
+
* 17
|
|
217
|
+
* 现在插入 7 , 会按广度优先的顺序插入在数组尾部
|
|
218
|
+
* 1
|
|
219
|
+
*
|
|
220
|
+
* 10 20
|
|
221
|
+
*
|
|
222
|
+
* 15 30 25 30
|
|
223
|
+
*
|
|
224
|
+
* 17 7
|
|
225
|
+
* 接着我们只需要将 7 逐层与上门的父节点比较, 7 较小则两者交互,一直让 7 上浮到适合的位置
|
|
226
|
+
*
|
|
227
|
+
* 0
|
|
228
|
+
*
|
|
229
|
+
* 1 2 2^0 得到第二层第一个索引
|
|
230
|
+
*
|
|
231
|
+
* 3 4 5 6 2^0 + 2^1 。。。 + 2^n + x = y
|
|
232
|
+
*
|
|
233
|
+
* 7 8
|
|
234
|
+
* 上浮后我们得到以上的树
|
|
235
|
+
*/
|
|
236
|
+
// 父子节点的关系
|
|
237
|
+
// 计算一个节点的 index 公式 ① 2^0 + 2^1 。。。 + 2^n + y = x 已知
|
|
238
|
+
// 节点的左子节点 index ② 2^0 + 2^1 。。。 + 2^n + 2^(n+1) + z = res 求 res
|
|
239
|
+
// ② - ① 得到 2^(n+1) + (z-y) = res - x
|
|
240
|
+
// 2^(n+1) + (z-y) + x = res
|
|
241
|
+
// 而 z 和 y 的关系是,③ z = 2y
|
|
242
|
+
const leakI = (y, max) => (y < 0 || y >= max ? null : y);
|
|
243
|
+
const getLeft = (x, max) => leakI(x * 2 + 1, max);
|
|
244
|
+
const getRight = (x, max) => leakI(x * 2 + 2, max);
|
|
245
|
+
const getParent = (x, max) => leakI((x - 1) >>> 1, max);
|
|
246
|
+
const exchange = (arr, i, j) => ([arr[i], arr[j]] = [arr[j], arr[i]]);
|
|
247
|
+
class PriorityQueue {
|
|
248
|
+
// 构造函数接受一个compare函数
|
|
249
|
+
// compare返回的-1, 0, 1决定元素是否优先被去除
|
|
250
|
+
constructor(aIsUrgent) {
|
|
251
|
+
this.aIsUrgent = aIsUrgent;
|
|
252
|
+
this.arr = [];
|
|
253
|
+
this.goUp = (arr, current, len) => {
|
|
254
|
+
let i = len - 1;
|
|
255
|
+
while (i > 0) {
|
|
256
|
+
const item = arr[i];
|
|
257
|
+
const pI = getParent(i, len);
|
|
258
|
+
const parent = arr[pI];
|
|
259
|
+
if (this.aIsUrgent(item, parent)) {
|
|
260
|
+
// console.log(`交换 parent:${parent} -> child:${item} `);
|
|
261
|
+
exchange(arr, i, pI);
|
|
262
|
+
// this.logTree();
|
|
263
|
+
i = pI;
|
|
264
|
+
}
|
|
265
|
+
else {
|
|
266
|
+
// console.log(`parent:${parent} child:${item} 不需要交换 \n`);
|
|
267
|
+
break;
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
};
|
|
271
|
+
this.goDown = (arr, i) => {
|
|
272
|
+
const len = this.size();
|
|
273
|
+
const half = len >>> 1;
|
|
274
|
+
while (i < half) {
|
|
275
|
+
const lI = getLeft(i, len);
|
|
276
|
+
const rI = getRight(i, len);
|
|
277
|
+
let point = i;
|
|
278
|
+
if (lI != null && this.aIsUrgent(arr[lI], arr[point])) {
|
|
279
|
+
point = lI;
|
|
280
|
+
}
|
|
281
|
+
if (rI != null && this.aIsUrgent(arr[rI], arr[point])) {
|
|
282
|
+
point = rI;
|
|
283
|
+
}
|
|
284
|
+
if (point === i) {
|
|
285
|
+
break;
|
|
286
|
+
}
|
|
287
|
+
// console.log(`交换 parent:${arr[i]} -> child:${arr[point]} `);
|
|
288
|
+
exchange(arr, i, point);
|
|
289
|
+
// this.logTree();
|
|
290
|
+
i = point;
|
|
291
|
+
}
|
|
292
|
+
};
|
|
293
|
+
}
|
|
294
|
+
// 添加一个元素
|
|
295
|
+
_add(current) {
|
|
296
|
+
// console.log(`加入 ${current}`);
|
|
297
|
+
this.arr.push(current);
|
|
298
|
+
const len = this.size();
|
|
299
|
+
// this.logTree();
|
|
300
|
+
if (len === 1) {
|
|
301
|
+
return;
|
|
302
|
+
}
|
|
303
|
+
this.goUp(this.arr, current, len);
|
|
304
|
+
}
|
|
305
|
+
add(...items) {
|
|
306
|
+
items.forEach(it => this._add(it));
|
|
307
|
+
}
|
|
308
|
+
// 去除头元素并返回
|
|
309
|
+
poll() {
|
|
310
|
+
const { arr } = this;
|
|
311
|
+
// console.log(`弹出 ${arr[0]} 把 ${arr[arr.length - 1]} 放置到队头 `);
|
|
312
|
+
const len = this.size();
|
|
313
|
+
if (len <= 2) {
|
|
314
|
+
return arr.shift();
|
|
315
|
+
}
|
|
316
|
+
const last = arr.pop();
|
|
317
|
+
const first = arr[0];
|
|
318
|
+
arr[0] = last;
|
|
319
|
+
// this.logTree();
|
|
320
|
+
this.goDown(this.arr, 0);
|
|
321
|
+
return first;
|
|
322
|
+
}
|
|
323
|
+
// 取得头元素
|
|
324
|
+
peek() {
|
|
325
|
+
return this.arr[0];
|
|
326
|
+
}
|
|
327
|
+
// 取得元素数量
|
|
328
|
+
size() {
|
|
329
|
+
return this.arr.length;
|
|
330
|
+
}
|
|
331
|
+
logTree() {
|
|
332
|
+
const { arr } = this;
|
|
333
|
+
let i = 0;
|
|
334
|
+
let j = 1;
|
|
335
|
+
let level = 0;
|
|
336
|
+
const matrix = [];
|
|
337
|
+
do {
|
|
338
|
+
matrix.push(arr.slice(i, j));
|
|
339
|
+
i = i * 2 + 1;
|
|
340
|
+
j = i + Math.pow(2, level) + 1;
|
|
341
|
+
level++;
|
|
342
|
+
} while (i < arr.length);
|
|
343
|
+
const last = Math.pow(2, matrix.length - 1);
|
|
344
|
+
const arrStr = JSON.stringify(last);
|
|
345
|
+
const halfLen = arrStr.length >>> 1;
|
|
346
|
+
matrix.forEach(it => {
|
|
347
|
+
const str = JSON.stringify(it);
|
|
348
|
+
const halfIt = str.length >>> 1;
|
|
349
|
+
console.log(str.padStart(halfLen + halfIt, ' '));
|
|
350
|
+
});
|
|
351
|
+
console.log('\n');
|
|
352
|
+
}
|
|
353
|
+
}
|
|
354
|
+
// case 1
|
|
355
|
+
// const pq = new PriorityQueue((a, b) => a - b)
|
|
356
|
+
// pq.add(5)
|
|
357
|
+
// pq.add(3)
|
|
358
|
+
// pq.add(1)
|
|
359
|
+
// pq.add(4)
|
|
360
|
+
// pq.add(2)
|
|
361
|
+
// const result = []
|
|
362
|
+
// while (pq.size() > 0) {
|
|
363
|
+
// result.push(pq.poll())
|
|
364
|
+
// }
|
|
365
|
+
// console.log(result);
|
|
366
|
+
// [1,2,3,4,5]
|
|
367
|
+
// case 2
|
|
368
|
+
// const pq = new PriorityQueue((a, b) => b - a)
|
|
369
|
+
// pq.add(1)
|
|
370
|
+
// pq.add(3)
|
|
371
|
+
// pq.add(4)
|
|
372
|
+
// pq.add(5)
|
|
373
|
+
// pq.add(2)
|
|
374
|
+
// const result = []
|
|
375
|
+
// while (pq.size() > 0) {
|
|
376
|
+
// result.push(pq.poll())
|
|
377
|
+
// }
|
|
378
|
+
// console.log(result);
|
|
379
|
+
// [5,4,3,2,1]
|
|
380
|
+
|
|
381
|
+
class TaskQueue {
|
|
382
|
+
constructor(callbackAble, aIsUrgent) {
|
|
383
|
+
this.callbackAble = callbackAble;
|
|
384
|
+
this.aIsUrgent = aIsUrgent;
|
|
385
|
+
this.isScheduling = false;
|
|
386
|
+
}
|
|
387
|
+
static create({ callbackAble, aIsUrgent }) {
|
|
388
|
+
const queue = new TaskQueue(callbackAble, aIsUrgent);
|
|
389
|
+
queue.taskQueue = new PriorityQueue(aIsUrgent);
|
|
390
|
+
return queue;
|
|
391
|
+
}
|
|
392
|
+
pushTask(task) {
|
|
393
|
+
const { taskQueue, isScheduling } = this;
|
|
394
|
+
taskQueue._add(task);
|
|
395
|
+
if (!isScheduling) {
|
|
396
|
+
this.callbackAble(this.scheduleTask.bind(this));
|
|
397
|
+
this.isScheduling = true;
|
|
398
|
+
}
|
|
399
|
+
}
|
|
400
|
+
scheduleTask() {
|
|
401
|
+
const { taskQueue } = this;
|
|
402
|
+
// console.log('调度 dispose');
|
|
403
|
+
const fn = taskQueue.peek();
|
|
404
|
+
if (!fn)
|
|
405
|
+
return (this.isScheduling = false);
|
|
406
|
+
const hasRemain = fn();
|
|
407
|
+
// 未完成
|
|
408
|
+
if (hasRemain) {
|
|
409
|
+
this.callbackAble(this.scheduleTask.bind(this));
|
|
410
|
+
return;
|
|
411
|
+
}
|
|
412
|
+
// 完成
|
|
413
|
+
taskQueue.poll();
|
|
414
|
+
evt.emit('one', fn);
|
|
415
|
+
if (taskQueue.size() === 0) {
|
|
416
|
+
evt.emit('done', fn);
|
|
417
|
+
return (this.isScheduling = false);
|
|
418
|
+
}
|
|
419
|
+
// 任务列表中还有任务
|
|
420
|
+
this.callbackAble(this.scheduleTask.bind(this));
|
|
421
|
+
}
|
|
422
|
+
}
|
|
423
|
+
|
|
424
|
+
const ide = globalThis.requestIdleCallback ||
|
|
425
|
+
(globalThis.requestAnimationFrame
|
|
426
|
+
? (fn) => globalThis.requestAnimationFrame(() => {
|
|
427
|
+
setTimeout(() => {
|
|
428
|
+
fn();
|
|
429
|
+
});
|
|
430
|
+
})
|
|
431
|
+
: globalThis.setTimeout);
|
|
432
|
+
const now = () => {
|
|
433
|
+
const timer = globalThis.performance || globalThis.Date;
|
|
434
|
+
return timer.now();
|
|
435
|
+
};
|
|
436
|
+
let channel = globalThis.MessageChannel ? new MessageChannel() : null;
|
|
437
|
+
if (globalThis.MessageChannel) {
|
|
438
|
+
channel = new MessageChannel();
|
|
439
|
+
}
|
|
440
|
+
let msgId = 0;
|
|
441
|
+
const macro = fn => {
|
|
442
|
+
if (!channel) {
|
|
443
|
+
setTimeout(fn);
|
|
444
|
+
}
|
|
445
|
+
const memoId = msgId;
|
|
446
|
+
function onMessage(e) {
|
|
447
|
+
if (memoId === e.data) {
|
|
448
|
+
fn();
|
|
449
|
+
channel.port2.removeEventListener('message', onMessage);
|
|
450
|
+
}
|
|
451
|
+
}
|
|
452
|
+
channel.port2.addEventListener('message', onMessage);
|
|
453
|
+
channel.port1.postMessage(msgId++);
|
|
454
|
+
};
|
|
455
|
+
const p = Promise.resolve();
|
|
456
|
+
const micro = (cb) => {
|
|
457
|
+
p.then(cb);
|
|
458
|
+
};
|
|
101
459
|
|
|
102
460
|
exports.Scheduler = void 0;
|
|
103
461
|
(function (Scheduler) {
|
|
@@ -110,7 +468,7 @@
|
|
|
110
468
|
[exports.Scheduler.Sync]: defaultScheduler,
|
|
111
469
|
[exports.Scheduler.Micro]: microScheduler,
|
|
112
470
|
[exports.Scheduler.Macro]: macroScheduler,
|
|
113
|
-
[exports.Scheduler.Layout]:
|
|
471
|
+
[exports.Scheduler.Layout]: layoutScheduler
|
|
114
472
|
};
|
|
115
473
|
const scheduler = (key, value) => (_scheduler[key] = value);
|
|
116
474
|
function defaultScheduler(effects) {
|
|
@@ -118,40 +476,21 @@
|
|
|
118
476
|
effect.runIfDirty();
|
|
119
477
|
}
|
|
120
478
|
}
|
|
121
|
-
|
|
122
|
-
let
|
|
479
|
+
let microSTaskQueue;
|
|
480
|
+
let macroSTaskQueue;
|
|
481
|
+
let layoutSTaskQueue;
|
|
123
482
|
function microScheduler(effects) {
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
p.then(() => {
|
|
127
|
-
defaultScheduler(effects);
|
|
128
|
-
hasMicroTask = false;
|
|
129
|
-
});
|
|
130
|
-
hasMicroTask = true;
|
|
131
|
-
}
|
|
132
|
-
let channel, macroQueue;
|
|
133
|
-
if (globalThis.MessageChannel) {
|
|
134
|
-
channel = new MessageChannel();
|
|
135
|
-
macroQueue = new Queue();
|
|
136
|
-
channel.port2.onmessage = () => {
|
|
137
|
-
while (macroQueue.first) {
|
|
138
|
-
macroQueue.shift()();
|
|
139
|
-
}
|
|
140
|
-
};
|
|
483
|
+
microSTaskQueue = microSTaskQueue || TaskQueue.create({ callbackAble: micro, aIsUrgent: (a, b) => a.time < b.time });
|
|
484
|
+
microSTaskQueue.pushTask(defaultScheduler.bind(undefined, effects));
|
|
141
485
|
}
|
|
142
486
|
function macroScheduler(effects) {
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
channel.port1.postMessage('');
|
|
146
|
-
}
|
|
147
|
-
setTimeout(() => {
|
|
148
|
-
defaultScheduler(effects);
|
|
149
|
-
});
|
|
487
|
+
macroSTaskQueue = macroSTaskQueue || TaskQueue.create({ callbackAble: macro, aIsUrgent: (a, b) => a.time < b.time });
|
|
488
|
+
macroSTaskQueue.pushTask(defaultScheduler.bind(undefined, effects));
|
|
150
489
|
}
|
|
151
|
-
function
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
490
|
+
function layoutScheduler(effects) {
|
|
491
|
+
layoutSTaskQueue =
|
|
492
|
+
layoutSTaskQueue || TaskQueue.create({ callbackAble: macro, aIsUrgent: (a, b) => a.time < b.time });
|
|
493
|
+
layoutSTaskQueue.pushTask(defaultScheduler.bind(undefined, effects));
|
|
155
494
|
}
|
|
156
495
|
|
|
157
496
|
const DefaultDFSOpt = {
|
|
@@ -366,262 +705,6 @@
|
|
|
366
705
|
}
|
|
367
706
|
}
|
|
368
707
|
|
|
369
|
-
/**
|
|
370
|
-
* 这是一个优先队列 (满足子节点总是比父节点大)
|
|
371
|
-
* 1
|
|
372
|
-
*
|
|
373
|
-
* 10 20
|
|
374
|
-
*
|
|
375
|
-
* 15 30 25 30
|
|
376
|
-
*
|
|
377
|
-
* 17
|
|
378
|
-
* 现在插入 7 , 会按广度优先的顺序插入在数组尾部
|
|
379
|
-
* 1
|
|
380
|
-
*
|
|
381
|
-
* 10 20
|
|
382
|
-
*
|
|
383
|
-
* 15 30 25 30
|
|
384
|
-
*
|
|
385
|
-
* 17 7
|
|
386
|
-
* 接着我们只需要将 7 逐层与上门的父节点比较, 7 较小则两者交互,一直让 7 上浮到适合的位置
|
|
387
|
-
*
|
|
388
|
-
* 0
|
|
389
|
-
*
|
|
390
|
-
* 1 2 2^0 得到第二层第一个索引
|
|
391
|
-
*
|
|
392
|
-
* 3 4 5 6 2^0 + 2^1 。。。 + 2^n + x = y
|
|
393
|
-
*
|
|
394
|
-
* 7 8
|
|
395
|
-
* 上浮后我们得到以上的树
|
|
396
|
-
*/
|
|
397
|
-
// 父子节点的关系
|
|
398
|
-
// 计算一个节点的 index 公式 ① 2^0 + 2^1 。。。 + 2^n + y = x 已知
|
|
399
|
-
// 节点的左子节点 index ② 2^0 + 2^1 。。。 + 2^n + 2^(n+1) + z = res 求 res
|
|
400
|
-
// ② - ① 得到 2^(n+1) + (z-y) = res - x
|
|
401
|
-
// 2^(n+1) + (z-y) + x = res
|
|
402
|
-
// 而 z 和 y 的关系是,③ z = 2y
|
|
403
|
-
const leakI = (y, max) => (y < 0 || y >= max ? null : y);
|
|
404
|
-
const getLeft = (x, max) => leakI(x * 2 + 1, max);
|
|
405
|
-
const getRight = (x, max) => leakI(x * 2 + 2, max);
|
|
406
|
-
const getParent = (x, max) => leakI((x - 1) >>> 1, max);
|
|
407
|
-
const exchange = (arr, i, j) => ([arr[i], arr[j]] = [arr[j], arr[i]]);
|
|
408
|
-
class PriorityQueue {
|
|
409
|
-
// 构造函数接受一个compare函数
|
|
410
|
-
// compare返回的-1, 0, 1决定元素是否优先被去除
|
|
411
|
-
constructor(aIsUrgent) {
|
|
412
|
-
this.aIsUrgent = aIsUrgent;
|
|
413
|
-
this.arr = [];
|
|
414
|
-
this.goUp = (arr, current, len) => {
|
|
415
|
-
let i = len - 1;
|
|
416
|
-
while (i > 0) {
|
|
417
|
-
const item = arr[i];
|
|
418
|
-
const pI = getParent(i, len);
|
|
419
|
-
const parent = arr[pI];
|
|
420
|
-
if (this.aIsUrgent(item, parent)) {
|
|
421
|
-
// console.log(`交换 parent:${parent} -> child:${item} `);
|
|
422
|
-
exchange(arr, i, pI);
|
|
423
|
-
// this.logTree();
|
|
424
|
-
i = pI;
|
|
425
|
-
}
|
|
426
|
-
else {
|
|
427
|
-
// console.log(`parent:${parent} child:${item} 不需要交换 \n`);
|
|
428
|
-
break;
|
|
429
|
-
}
|
|
430
|
-
}
|
|
431
|
-
};
|
|
432
|
-
this.goDown = (arr, i) => {
|
|
433
|
-
const len = this.size();
|
|
434
|
-
const half = len >>> 1;
|
|
435
|
-
while (i < half) {
|
|
436
|
-
const lI = getLeft(i, len);
|
|
437
|
-
const rI = getRight(i, len);
|
|
438
|
-
let point = i;
|
|
439
|
-
if (lI != null && this.aIsUrgent(arr[lI], arr[point])) {
|
|
440
|
-
point = lI;
|
|
441
|
-
}
|
|
442
|
-
if (rI != null && this.aIsUrgent(arr[rI], arr[point])) {
|
|
443
|
-
point = rI;
|
|
444
|
-
}
|
|
445
|
-
if (point === i) {
|
|
446
|
-
break;
|
|
447
|
-
}
|
|
448
|
-
// console.log(`交换 parent:${arr[i]} -> child:${arr[point]} `);
|
|
449
|
-
exchange(arr, i, point);
|
|
450
|
-
// this.logTree();
|
|
451
|
-
i = point;
|
|
452
|
-
}
|
|
453
|
-
};
|
|
454
|
-
}
|
|
455
|
-
// 添加一个元素
|
|
456
|
-
_add(current) {
|
|
457
|
-
// console.log(`加入 ${current}`);
|
|
458
|
-
this.arr.push(current);
|
|
459
|
-
const len = this.size();
|
|
460
|
-
// this.logTree();
|
|
461
|
-
if (len === 1) {
|
|
462
|
-
return;
|
|
463
|
-
}
|
|
464
|
-
this.goUp(this.arr, current, len);
|
|
465
|
-
}
|
|
466
|
-
add(...items) {
|
|
467
|
-
items.forEach(it => this._add(it));
|
|
468
|
-
}
|
|
469
|
-
// 去除头元素并返回
|
|
470
|
-
poll() {
|
|
471
|
-
const { arr } = this;
|
|
472
|
-
// console.log(`弹出 ${arr[0]} 把 ${arr[arr.length - 1]} 放置到队头 `);
|
|
473
|
-
const len = this.size();
|
|
474
|
-
if (len <= 2) {
|
|
475
|
-
return arr.shift();
|
|
476
|
-
}
|
|
477
|
-
const last = arr.pop();
|
|
478
|
-
const first = arr[0];
|
|
479
|
-
arr[0] = last;
|
|
480
|
-
// this.logTree();
|
|
481
|
-
this.goDown(this.arr, 0);
|
|
482
|
-
return first;
|
|
483
|
-
}
|
|
484
|
-
// 取得头元素
|
|
485
|
-
peek() {
|
|
486
|
-
return this.arr[0];
|
|
487
|
-
}
|
|
488
|
-
// 取得元素数量
|
|
489
|
-
size() {
|
|
490
|
-
return this.arr.length;
|
|
491
|
-
}
|
|
492
|
-
logTree() {
|
|
493
|
-
const { arr } = this;
|
|
494
|
-
let i = 0;
|
|
495
|
-
let j = 1;
|
|
496
|
-
let level = 0;
|
|
497
|
-
const matrix = [];
|
|
498
|
-
do {
|
|
499
|
-
matrix.push(arr.slice(i, j));
|
|
500
|
-
i = i * 2 + 1;
|
|
501
|
-
j = i + Math.pow(2, level) + 1;
|
|
502
|
-
level++;
|
|
503
|
-
} while (i < arr.length);
|
|
504
|
-
const last = Math.pow(2, matrix.length - 1);
|
|
505
|
-
const arrStr = JSON.stringify(last);
|
|
506
|
-
const halfLen = arrStr.length >>> 1;
|
|
507
|
-
matrix.forEach(it => {
|
|
508
|
-
const str = JSON.stringify(it);
|
|
509
|
-
const halfIt = str.length >>> 1;
|
|
510
|
-
console.log(str.padStart(halfLen + halfIt, ' '));
|
|
511
|
-
});
|
|
512
|
-
console.log('\n');
|
|
513
|
-
}
|
|
514
|
-
}
|
|
515
|
-
// case 1
|
|
516
|
-
// const pq = new PriorityQueue((a, b) => a - b)
|
|
517
|
-
// pq.add(5)
|
|
518
|
-
// pq.add(3)
|
|
519
|
-
// pq.add(1)
|
|
520
|
-
// pq.add(4)
|
|
521
|
-
// pq.add(2)
|
|
522
|
-
// const result = []
|
|
523
|
-
// while (pq.size() > 0) {
|
|
524
|
-
// result.push(pq.poll())
|
|
525
|
-
// }
|
|
526
|
-
// console.log(result);
|
|
527
|
-
// [1,2,3,4,5]
|
|
528
|
-
// case 2
|
|
529
|
-
// const pq = new PriorityQueue((a, b) => b - a)
|
|
530
|
-
// pq.add(1)
|
|
531
|
-
// pq.add(3)
|
|
532
|
-
// pq.add(4)
|
|
533
|
-
// pq.add(5)
|
|
534
|
-
// pq.add(2)
|
|
535
|
-
// const result = []
|
|
536
|
-
// while (pq.size() > 0) {
|
|
537
|
-
// result.push(pq.poll())
|
|
538
|
-
// }
|
|
539
|
-
// console.log(result);
|
|
540
|
-
// [5,4,3,2,1]
|
|
541
|
-
|
|
542
|
-
class TaskQueue {
|
|
543
|
-
constructor(callbackAble, aIsUrgent) {
|
|
544
|
-
this.callbackAble = callbackAble;
|
|
545
|
-
this.aIsUrgent = aIsUrgent;
|
|
546
|
-
this.isScheduling = false;
|
|
547
|
-
}
|
|
548
|
-
static create({ callbackAble, aIsUrgent }) {
|
|
549
|
-
const queue = new TaskQueue(callbackAble, aIsUrgent);
|
|
550
|
-
queue.taskQueue = new PriorityQueue(aIsUrgent);
|
|
551
|
-
return queue;
|
|
552
|
-
}
|
|
553
|
-
pushTask(task) {
|
|
554
|
-
const { taskQueue, isScheduling } = this;
|
|
555
|
-
taskQueue._add(task);
|
|
556
|
-
if (!isScheduling) {
|
|
557
|
-
this.callbackAble(this.scheduleTask.bind(this));
|
|
558
|
-
this.isScheduling = true;
|
|
559
|
-
}
|
|
560
|
-
}
|
|
561
|
-
scheduleTask() {
|
|
562
|
-
const { taskQueue } = this;
|
|
563
|
-
// console.log('调度 dispose');
|
|
564
|
-
const fn = taskQueue.peek();
|
|
565
|
-
if (!fn)
|
|
566
|
-
return (this.isScheduling = false);
|
|
567
|
-
const hasRemain = fn();
|
|
568
|
-
// 未完成
|
|
569
|
-
if (hasRemain) {
|
|
570
|
-
this.callbackAble(this.scheduleTask.bind(this));
|
|
571
|
-
return;
|
|
572
|
-
}
|
|
573
|
-
// 完成
|
|
574
|
-
taskQueue.poll();
|
|
575
|
-
if (taskQueue.size() === 0)
|
|
576
|
-
return (this.isScheduling = false);
|
|
577
|
-
// 任务列表中还有任务
|
|
578
|
-
this.callbackAble(this.scheduleTask.bind(this));
|
|
579
|
-
}
|
|
580
|
-
}
|
|
581
|
-
|
|
582
|
-
const ide = globalThis.requestIdleCallback ||
|
|
583
|
-
(globalThis.requestAnimationFrame
|
|
584
|
-
? (fn) => globalThis.requestAnimationFrame(() => {
|
|
585
|
-
setTimeout(() => {
|
|
586
|
-
fn();
|
|
587
|
-
});
|
|
588
|
-
})
|
|
589
|
-
: globalThis.setTimeout);
|
|
590
|
-
const now = () => {
|
|
591
|
-
const timer = globalThis.performance || globalThis.Date;
|
|
592
|
-
return timer.now();
|
|
593
|
-
};
|
|
594
|
-
|
|
595
|
-
// export class IdeScheduler {
|
|
596
|
-
// constructor() {}
|
|
597
|
-
// isScheduling = false;
|
|
598
|
-
// taskQueue = new Queue<Function>();
|
|
599
|
-
// pushTask(task: Function) {
|
|
600
|
-
// const { taskQueue, isScheduling } = this;
|
|
601
|
-
// taskQueue.push(task);
|
|
602
|
-
// if (!isScheduling) {
|
|
603
|
-
// ide(this.scheduleTask.bind(this));
|
|
604
|
-
// this.isScheduling = true;
|
|
605
|
-
// }
|
|
606
|
-
// }
|
|
607
|
-
// scheduleTask() {
|
|
608
|
-
// const { taskQueue } = this;
|
|
609
|
-
// // console.log('调度 dispose');
|
|
610
|
-
// const fn = taskQueue.first;
|
|
611
|
-
// if (!fn) return (this.isScheduling = false);
|
|
612
|
-
// const hasRemain = fn();
|
|
613
|
-
// // 未完成
|
|
614
|
-
// if (hasRemain) {
|
|
615
|
-
// ide(this.scheduleTask.bind(this));
|
|
616
|
-
// return;
|
|
617
|
-
// }
|
|
618
|
-
// // 完成
|
|
619
|
-
// taskQueue.shift();
|
|
620
|
-
// if (taskQueue.len === 0) return (this.isScheduling = false);
|
|
621
|
-
// // 任务列表中还有任务
|
|
622
|
-
// ide(this.scheduleTask.bind(this));
|
|
623
|
-
// }
|
|
624
|
-
// }
|
|
625
708
|
/** scope 捕获,引用外部 signal 孤岛 */
|
|
626
709
|
const unTrackIsland = (signal) => {
|
|
627
710
|
// 原来是孤岛,且被 scope 管理的要恢复
|
|
@@ -640,6 +723,14 @@
|
|
|
640
723
|
Line.line_line_rec(recEnd, line);
|
|
641
724
|
}
|
|
642
725
|
};
|
|
726
|
+
/** 子 scope 释放,把其 only 被其持有的 signal 挂回其属于的 scope */
|
|
727
|
+
const trackByOtherScopeDispose = (signal) => {
|
|
728
|
+
const line = new Line();
|
|
729
|
+
const { recEnd } = signal.scope;
|
|
730
|
+
Line.emit_line(signal, line);
|
|
731
|
+
Line.rec_line(signal.scope, line);
|
|
732
|
+
Line.line_line_rec(recEnd, line);
|
|
733
|
+
};
|
|
643
734
|
const markOutLink = (signal, downstream) => {
|
|
644
735
|
// 上游是外部节点,或者上游引用了外部节点的, 做传播
|
|
645
736
|
if (signal.scope !== downstream.scope || signal.state & State.OutLink) {
|
|
@@ -661,13 +752,16 @@
|
|
|
661
752
|
return a.index < b.index;
|
|
662
753
|
}
|
|
663
754
|
});
|
|
664
|
-
function handleOneTask(
|
|
755
|
+
function handleOneTask(scope, breakStack) {
|
|
665
756
|
breakStack = remain.stack || breakStack;
|
|
666
757
|
// 将 s 同步到 remainRoot
|
|
667
758
|
let lineToRemove = null;
|
|
668
759
|
const startTime = now();
|
|
760
|
+
if (scope.emitStart) {
|
|
761
|
+
Line.unlink(scope.emitStart);
|
|
762
|
+
}
|
|
669
763
|
try {
|
|
670
|
-
dfs(
|
|
764
|
+
dfs(scope, {
|
|
671
765
|
breakStack,
|
|
672
766
|
breakNode: remain.node,
|
|
673
767
|
breakLine: remain.line,
|
|
@@ -685,9 +779,28 @@
|
|
|
685
779
|
};
|
|
686
780
|
throw BreakErr;
|
|
687
781
|
}
|
|
782
|
+
// 1. 未标记的节点,是外部节点
|
|
688
783
|
if (!(node.state & State.OutLink)) {
|
|
689
784
|
return true;
|
|
690
785
|
}
|
|
786
|
+
// 2. 标记的节点,但是 scope 不一样,说明外部节点也引用了 另一 scope 的节点
|
|
787
|
+
if (lineFromUp && node.scope !== lineFromUp.downstream['scope']) {
|
|
788
|
+
// 是仅被 node 引用的外部节点
|
|
789
|
+
if (node.emitStart === node.emitEnd) {
|
|
790
|
+
// 已经 abort 只能继续释放
|
|
791
|
+
if (scope.state & State.ScopeAborted) {
|
|
792
|
+
const bound = handleOneTask.bind(undefined, node, []);
|
|
793
|
+
bound.index = G.scopeDisposeI++;
|
|
794
|
+
ideScheduler.pushTask(bound);
|
|
795
|
+
}
|
|
796
|
+
// 可以将其交给 原 scope 释放
|
|
797
|
+
else {
|
|
798
|
+
trackByOtherScopeDispose(node);
|
|
799
|
+
}
|
|
800
|
+
}
|
|
801
|
+
// 任何外部引用都应该被断开
|
|
802
|
+
return true;
|
|
803
|
+
}
|
|
691
804
|
// 对于嵌套作用域不允许重复进入
|
|
692
805
|
node.state &= ~State.OutLink;
|
|
693
806
|
},
|
|
@@ -711,6 +824,7 @@
|
|
|
711
824
|
node: null,
|
|
712
825
|
line: null
|
|
713
826
|
};
|
|
827
|
+
scope.state |= State.ScopeAborted;
|
|
714
828
|
}
|
|
715
829
|
catch (error) {
|
|
716
830
|
if (error === BreakErr)
|
|
@@ -962,7 +1076,7 @@
|
|
|
962
1076
|
// scope 被取消
|
|
963
1077
|
(this.scope && this.scope.state & State.ScopeAbort) ||
|
|
964
1078
|
// 是 scope 节点,且处于 ready 状态,不需要重复执行
|
|
965
|
-
(this === this.scope && this.state &
|
|
1079
|
+
(this === this.scope && this.state & ScopeExecuted));
|
|
966
1080
|
}
|
|
967
1081
|
}
|
|
968
1082
|
Signal.Pulling = null;
|
|
@@ -1039,8 +1153,54 @@
|
|
|
1039
1153
|
return s;
|
|
1040
1154
|
});
|
|
1041
1155
|
};
|
|
1156
|
+
// const globalSignal = $(10);
|
|
1157
|
+
// let outerA, outerB, innerX, innerY, outerResult, innerResult, innerDispose;
|
|
1158
|
+
// const outerDispose = scope(() => {
|
|
1159
|
+
// outerA = $(1);
|
|
1160
|
+
// outerB = $(2);
|
|
1161
|
+
// // 外层计算信号
|
|
1162
|
+
// outerResult = $(() => {
|
|
1163
|
+
// const res = globalSignal.v + outerA.v + outerB.v;
|
|
1164
|
+
// return res;
|
|
1165
|
+
// });
|
|
1166
|
+
// innerDispose = scope(() => {
|
|
1167
|
+
// innerX = $(3);
|
|
1168
|
+
// innerY = $(4);
|
|
1169
|
+
// // 内层计算信号,既依赖内层也依赖外层信号
|
|
1170
|
+
// innerResult = $(() => {
|
|
1171
|
+
// const res = outerA.v + innerX.v + innerY.v;
|
|
1172
|
+
// return res;
|
|
1173
|
+
// });
|
|
1174
|
+
// // 访问信号以建立依赖关系
|
|
1175
|
+
// innerResult();
|
|
1176
|
+
// });
|
|
1177
|
+
// // 访问外层信号
|
|
1178
|
+
// outerResult();
|
|
1179
|
+
// // 将内层dispose函数绑定到外层scope,这样可以测试嵌套行为
|
|
1180
|
+
// (outerResult as any).innerDispose = innerDispose;
|
|
1181
|
+
// });
|
|
1182
|
+
// outerA.v = 5;
|
|
1183
|
+
// innerX.v = 6;
|
|
1184
|
+
// globalSignal.v = 20;
|
|
1185
|
+
// // 先释放内层scope
|
|
1186
|
+
// innerDispose();
|
|
1187
|
+
// innerX.v = 7;
|
|
1188
|
+
// outerA.v = 8;
|
|
1189
|
+
// outerDispose();
|
|
1190
|
+
// evt.on('one', ({ index }) => {
|
|
1191
|
+
// switch (index) {
|
|
1192
|
+
// case 0:
|
|
1193
|
+
// console.log({ index });
|
|
1194
|
+
// break;
|
|
1195
|
+
// case 1:
|
|
1196
|
+
// console.log({ index });
|
|
1197
|
+
// default:
|
|
1198
|
+
// break;
|
|
1199
|
+
// }
|
|
1200
|
+
// });
|
|
1042
1201
|
|
|
1043
1202
|
exports.$ = $;
|
|
1203
|
+
exports.TaskQueue = TaskQueue;
|
|
1044
1204
|
exports.customSignal = customSignal;
|
|
1045
1205
|
exports.scheduler = scheduler;
|
|
1046
1206
|
exports.scope = scope;
|