lib.b.data.ts 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494
  1. type _get_callback_<T, prop extends keyof T> = (proxy: T, value: T[prop])=>T[prop];
  2. type _set_callback_<T, prop extends keyof T> = (proxy: T, old: T[prop], value: T[prop])=>T[prop];
  3. const __Global__ = window ?? globalThis;
  4. /** 代理拦截数据 */
  5. export type DataBlocker<T> = {
  6. get?: {[prop in keyof T]?: _get_callback_<T, prop>}&{ignore?: Array<mtec.OmitKeys<T, Function>>},
  7. set?: {[prop in keyof T]?: _set_callback_<T, prop>}&{ignore?: Array<mtec.OmitKeys<T, Function>>},
  8. afterSet?: (prop: string|symbol, old: any, value: any, hdl: PxiHandle<T>)=>void;
  9. }
  10. export class PxiHandle<T extends Object|Array<any>> implements ProxyHandler<T>{
  11. constructor(private tgt: T, private name: string, private prt: PxiHandle<any>, private blocker?:DataBlocker<T>){
  12. if(blocker) this.in(blocker);
  13. this.revocable = Proxy.revocable(tgt, this);
  14. this.old = (Array.isArray(tgt)?[]:{}) as T;
  15. this.backup();
  16. }
  17. private getGetCall<prop extends keyof T>(key: prop): _get_callback_<T, prop>{
  18. let call: _get_callback_<T, prop>;
  19. if(this.blocker && this.blocker.get && Reflect.has(this.blocker.get, key)) call = Reflect.get(this.blocker.get, key) as _get_callback_<T, prop>;
  20. return call;
  21. }
  22. private getSetCall<prop extends keyof T>(key: prop): _set_callback_<T, prop>{
  23. let call: _set_callback_<T, prop>;
  24. if(this.blocker && this.blocker.set && Reflect.has(this.blocker.set, key)) call = Reflect.get(this.blocker.set, key) as _set_callback_<T, prop>;
  25. return call;
  26. }
  27. private backup(key?: any){
  28. let value: any;
  29. if(key===undefined){
  30. value = mtec.cloneData(this.target);
  31. this.old = value;
  32. }else{
  33. value = Reflect.get(this.target, key);
  34. value = mtec.cloneData(value);
  35. Reflect.set(this.old, key, value);
  36. }
  37. return value;
  38. }
  39. valueOf(){
  40. let temp: any;
  41. if(Array.isArray(this.tgt)){
  42. temp = [];
  43. let proxy = this.proxy as Array<any>;
  44. for(let i = 0; i < proxy.length; i++) temp.push(Reflect.get(proxy, i));
  45. }else{
  46. temp = {};
  47. let proxy = this.proxy as Object;
  48. Reflect.ownKeys(proxy).forEach(key=>Reflect.set(temp, key, Reflect.get(proxy, key)));
  49. }
  50. return temp as T;
  51. }
  52. private async publish(key: string|symbol, old: any, value: any){
  53. if(key!==undefined){
  54. let path = this.path + '.' + String(key);
  55. DataProxy.publish(path, old, value);
  56. }
  57. if(this.parent) this.parent.publish(this.name, this.old, this.target);
  58. }
  59. public in(blocker: DataBlocker<T>){
  60. if(this.blocker === undefined) this.blocker = {};
  61. let props: any[] = [];
  62. if(Reflect.has(blocker, 'get')){
  63. if(this.blocker.get === undefined) this.blocker.get = {};
  64. Reflect.ownKeys(blocker.get).forEach(p=>{
  65. Reflect.set(this.blocker.get, p, Reflect.get(blocker.get, p));
  66. if(!props.includes(p)) props.push(p);
  67. });
  68. }
  69. if(Reflect.has(blocker, 'set')){
  70. if(this.blocker.set === undefined) this.blocker.set = {};
  71. Reflect.ownKeys(blocker.set).forEach(p=>{
  72. Reflect.set(this.blocker.set, p, Reflect.get(blocker.set, p));
  73. if(!props.includes(p)) props.push(p);
  74. });
  75. }
  76. Reflect.ownKeys(blocker).filter(k=>!['get', 'set'].includes(k as any))
  77. .forEach(p=>Reflect.has(blocker, p)?Reflect.set(this.blocker, p, Reflect.get(blocker, p)):void 0);
  78. props.filter(p=>{
  79. let value = Reflect.get(this.tgt, p);
  80. return typeof value === 'object' && value !== null;
  81. }).forEach(p=>{
  82. let value = Reflect.get(this.tgt, p);
  83. if(!this.children.has(p) || this.children.get(p).target!==value){
  84. let handle = DataProxy.proxy(value, String(p), this).handle;
  85. this.children.set(p, handle);
  86. }
  87. });
  88. }
  89. /** 销毁所有代理 */
  90. public revoke(){
  91. this.children.forEach(h=>h.revoke());
  92. this.children.clear();
  93. DataProxy.revoke(this);
  94. this.revocable.revoke();
  95. }
  96. get(target: T, prop: string|symbol, receiver: any): T[keyof T]{
  97. let value: any;
  98. if(this.blocker?.get?.ignore?.includes(prop as any)) value = Reflect.get(target, prop);
  99. else{
  100. value = this.children.has(prop) ? this.children.get(prop).proxy : Reflect.get(target, prop);
  101. if(typeof value === 'function') value = Reflect.apply(Function.bind, value, [receiver]);
  102. else{
  103. let call = this.getGetCall(prop as any);
  104. value = call ? call(receiver, value) : value;
  105. }
  106. }
  107. if(typeof value === 'object' && value !== null && !this.children.has(prop)){
  108. let handle = DataProxy.proxy(value, String(prop), this).handle;
  109. this.children.set(prop, handle);
  110. }
  111. return value;
  112. }
  113. set(target: T, prop: string|symbol, value: any, receiver: any): boolean{
  114. if(this.blocker && this.blocker.set && this.blocker.set.ignore && this.blocker.set.ignore.includes(prop as any)){
  115. Reflect.set(target, prop, undefined);
  116. return true;
  117. }
  118. let old = this.backup(prop);
  119. let call = this.getSetCall(prop as any);
  120. if(typeof value === 'object' && value !== null && !this.children.has(prop)){
  121. let handle = DataProxy.proxy(value, String(prop), this).handle;
  122. this.children.set(prop, handle);
  123. }
  124. let new_value = call ? call(receiver, old, value) : value;
  125. if(this.children.has(prop) && this.children.get(prop).target !== new_value){
  126. let hdl = this.children.get(prop);
  127. if(!new_value){
  128. hdl.revoke();
  129. this.children.delete(prop);
  130. }else if(Array.isArray(new_value)){
  131. hdl.proxy.splice(0, hdl.proxy.length);
  132. hdl.proxy.push(...new_value);
  133. }else Object.assign(hdl.proxy, new_value);
  134. }
  135. Reflect.set(target, prop, this.children.has(prop) ? this.children.get(prop).valueOf() : new_value);
  136. if(this.blocker?.afterSet) this.blocker.afterSet(prop, old, new_value, this);
  137. this.publish(prop, old, new_value);
  138. return true;
  139. }
  140. private revocable: ReturnType<typeof Proxy.revocable>;
  141. private old: T;
  142. /** 本级代理 */
  143. public get proxy(){
  144. return this.revocable.proxy as T;
  145. }
  146. /** 代理对象 */
  147. public get target(){
  148. return this.tgt;
  149. }
  150. /** 父级handle */
  151. public get parent(){
  152. return this.prt;
  153. }
  154. /** 子级handle */
  155. private children = new Map<any, PxiHandle<any>>();
  156. /** 当前handle的路径 */
  157. public get path(): string{
  158. return this.parent ? this.parent.path+'.'+this.name : this.name;
  159. }
  160. }
  161. export class DataProxy{
  162. /** 代理句柄映射 */
  163. private static id_map: Map<any, string> = new Map();
  164. private static record: Map<string, [any, any, PxiHandle<any>]> = new Map();
  165. /**
  166. * 生成代理对象
  167. * @param ins 数据实例
  168. * @param name 数据名称
  169. */
  170. public static proxy<I extends Object>(ins: I, name?: string, parent: PxiHandle<any> = null){
  171. let proxy: I, handle: PxiHandle<I>;
  172. if(DataProxy.id_map.has(ins)){
  173. let id = DataProxy.id_map.get(ins);
  174. let [target, pxi, handle] = DataProxy.record.get(id);
  175. proxy = pxi, handle = handle;
  176. }else{
  177. let id = mtec.string.randomToken(8, 36, token=>!DataProxy.record.has(token));
  178. handle = new PxiHandle(ins, name??id, parent);
  179. proxy = handle.proxy;
  180. [ins, handle, handle.proxy].forEach(i=>DataProxy.id_map.set(i, id));
  181. DataProxy.record.set(id, [ins, handle.proxy, handle]);
  182. }
  183. return {proxy, handle};
  184. }
  185. /** 是否已注册代理 */
  186. public static hasProxy(ins: any){
  187. return DataProxy.id_map.has(ins);
  188. }
  189. /**
  190. * 初始化一个代理
  191. * @param target 代理对象
  192. * @param save 是否需要本地存储
  193. * @param init 初始化句柄的回调
  194. * @returns
  195. */
  196. public static initProxy<D>(name: string, target: D, save: boolean = false, blocker?: DataBlocker<D>, prefix?: string){
  197. let result = DataProxy.proxy(target, name);
  198. if(save){
  199. let item_key = (prefix??'')+name;
  200. mtec.local.read(item_key, target);
  201. result.handle.in({
  202. afterSet(prop, old, valu, handle){
  203. mtec.local.save(item_key, handle.target);
  204. }
  205. });
  206. }
  207. if(blocker) result.handle.in(blocker);
  208. Reflect.set(__Global__, 'data_'+name, result.proxy);
  209. return result.proxy
  210. }
  211. /** 销毁监听代理和记录 */
  212. public static revoke<D extends Object>(data: D){
  213. if(DataProxy.id_map.has(data)){
  214. let id = DataProxy.id_map.get(data);
  215. let [ins, pxi, handle] = DataProxy.record.get(id);
  216. [ins, pxi, handle].forEach(i=>DataProxy.id_map.delete(i));
  217. let path_list: string[] = [];
  218. let path = handle.path;
  219. DataProxy.call_pool.forEach((l, p)=>p.startsWith(path)?path_list.push(p):void 0);
  220. path_list.forEach(p=>DataProxy.call_pool.delete(p));
  221. handle.revoke();
  222. DataProxy.record.delete(id);
  223. }
  224. }
  225. private static monitor_call = new Map<string, Array<(path: string, old: any, value: any)=>void>>();
  226. public static monitor(data: Object, call: (path: string, old: any, value: any)=>void){
  227. let id = DataProxy.id_map.get(data);
  228. if(!DataProxy.record.has(id)){
  229. mtec.log.tag('无法对未代理的实例进行监听: orange', data);
  230. return void 0;
  231. }
  232. let [ins, pxi, hdl] = DataProxy.record.get(id);
  233. let path = hdl.path;
  234. if(!DataProxy.monitor_call.has(path)){
  235. DataProxy.monitor_call.set(path, []);
  236. }
  237. DataProxy.monitor_call.get(path).push(call);
  238. }
  239. public static free(data: Object){
  240. let id = DataProxy.id_map.get(data);
  241. if(!DataProxy.record.has(id)){
  242. return void 0;
  243. }
  244. let [ins, pxi, hdl] = DataProxy.record.get(id);
  245. let path = hdl.path;
  246. if(DataProxy.monitor_call.has(path)){
  247. DataProxy.monitor_call.delete(path);
  248. }
  249. }
  250. /** 监听回调池 */
  251. private static call_pool: Map<string, Function[]> = new Map();
  252. /**
  253. * 注册监听
  254. * @param data 要监听的数据
  255. * @param key 要监听的字段
  256. * @param call 更新时触发的回调函数
  257. * @param update 是否在注册时触发回调
  258. */
  259. public static follow<D extends Object, K extends mtec.OmitKeys<D, Function>>(data: D, key: K, call: (old_value: D[K], new_value: D[K])=>void, update: boolean = true){
  260. let handle: PxiHandle<D>;
  261. if(DataProxy.id_map.has(data)){
  262. handle = DataProxy.record.get(DataProxy.id_map.get(data))[2];
  263. }else{
  264. mtec.log.tag('无法对未代理的实例进行监听: orange', data);
  265. return void 0;
  266. }
  267. let path = handle.path + '.' + key.toString();
  268. if(!DataProxy.call_pool.has(path)){
  269. DataProxy.call_pool.set(path, []);
  270. }
  271. DataProxy.call_pool.get(path).push(call);
  272. if(update){
  273. let [target, proxy] = DataProxy.record.get(DataProxy.id_map.get(data));
  274. call(target[key], proxy[key]);
  275. }
  276. return {path, call} as {path: string, call: Function};
  277. }
  278. /** 注销监听 */
  279. public static cancel(info: ReturnType<typeof DataProxy.follow>){
  280. if(!DataProxy.call_pool.has(info.path)){
  281. return void 0;
  282. }
  283. let list = DataProxy.call_pool.get(info.path);
  284. if(list.length > 0){
  285. list.splice(list.indexOf(info.call), 1);
  286. }
  287. if(list.length <= 0){
  288. DataProxy.call_pool.delete(info.path);
  289. }
  290. }
  291. /**
  292. * 取消指定的监听
  293. * @param data 要监听的数据
  294. * @param key 要监听的字段
  295. * @param call 更新时触发的回调函数
  296. */
  297. public static out<D extends Object, K extends mtec.OmitKeys<D, Function>>(data: D, key: K, call: (old_value: D[K], new_value: D[K])=>void){
  298. let handle: PxiHandle<D> = DataProxy.record.get(DataProxy.id_map.get(data))[2];
  299. if(handle){
  300. let path = handle.path + '.' + key.toString();
  301. DataProxy.cancel({path, call});
  302. }
  303. }
  304. /** 指定的路径是否被监听 */
  305. public static hasFollow<D extends Object, K extends mtec.OmitKeys<D, Function>>(data: D, key: K){
  306. let id = DataProxy.id_map.get(data);
  307. if(id===undefined){
  308. return false;
  309. }
  310. let handle = DataProxy.record.get(id)[2];
  311. if(handle===undefined){
  312. return false;
  313. }
  314. let path = handle.path + '.' + key.toString();
  315. let list = DataProxy.call_pool.get(path);
  316. return list===undefined ? false : list.length > 0;
  317. }
  318. /** 是否有任务正在执行中 */
  319. private static tasking: boolean = false;
  320. /** 更新日志记录 */
  321. private static record_map = new Map<string, [any, any]>();
  322. /** 动态延迟,为了解决子节点并发更新时,父节点更新的重复触发造成的性能浪费,逻辑上不能完全防止,目前没有发现异常,暂时如此 */
  323. private static delay = (()=>{
  324. let c = createCountAverageIns();
  325. c.add(30);
  326. return c;
  327. })();
  328. private static delay_stamp = performance.now();
  329. private static updateDelay(){
  330. let now = performance.now();
  331. let diff = now-DataProxy.delay_stamp;
  332. DataProxy.delay_stamp = now;
  333. if(diff<=30 && diff>0) DataProxy.delay.add(diff);
  334. }
  335. // ⬆⬆⬆ 该段代码的是为了防止,自节点的并发更新,导致父节点的更新事件重复触发的问题
  336. /** 打包生成任务 */
  337. private static packaging(){
  338. if(DataProxy.record_map.size===0) return void 0;
  339. let record = Array.from(DataProxy.record_map);
  340. DataProxy.record_map.clear();
  341. let list = record.filter(el=>DataProxy.call_pool.has(el[0])).map(item=>{
  342. return {
  343. path: item[0],
  344. data: item[1]
  345. }
  346. });
  347. if(list.length>0) DataProxy.taskLoop(list);
  348. else DataProxy.tasking = false;
  349. }
  350. /** 循环执行任务列表中的任务 */
  351. private static async taskLoop(list: {path: string, data: [any, any]}[]){
  352. while(list.length>0){
  353. let task = list.shift();
  354. //if(!task.path.includes('clock')) abi.log.tag('run task: green', task.path, ...task.data);
  355. await Promise.allSettled(DataProxy.call_pool.get(task.path).map(f=>new Promise((s, j)=>{
  356. f(...task.data);
  357. s(void 0);
  358. })));
  359. DataProxy.distributeTask(task);
  360. }
  361. if(DataProxy.record_map.size > 0) DataProxy.packaging();
  362. else DataProxy.tasking = false;
  363. }
  364. /** 触发一个任务支线 */
  365. private static async distributeTask(task: {path: string, data: [any, any]}){
  366. let path_list = task.path.split('.');
  367. // 触发被标记的代理监听
  368. if(DataProxy.monitor_call.has(path_list[0])) DataProxy.monitor_call.get(path_list[0]).forEach(f=>f(task.path, ...task.data));
  369. }
  370. /**
  371. * 发布更新日志
  372. * @param path 更新路径
  373. * @param old 原始值
  374. * @param value 更新值
  375. */
  376. public static publish(path: string, old: any, value: any){
  377. //abi.log.tag('publish-->:blue', DataTransfer.tasking, path, JSON.stringify([old, value]));
  378. DataProxy.record_map.set(path, [old, value]);
  379. if(!DataProxy.tasking){
  380. DataProxy.tasking = true;
  381. // ⬇⬇⬇ 该段代码的是为了防止,字段的并发更新,导致父节点的更新事件重复触发的问题
  382. let id = setTimeout((dm: typeof DataProxy)=>{
  383. dm.packaging();
  384. clearTimeout(id);
  385. }, DataProxy.delay.average, DataProxy);
  386. }else DataProxy.updateDelay();
  387. // ⬆⬆⬆ 该段代码的是为了防止,字段的并发更新,导致父节点的更新事件重复触发的问题
  388. }
  389. }
  390. function createCountAverageIns(){
  391. return new ((()=>{
  392. if(Reflect.has(window ?? globalThis, 'hnc') && mtec.CountAverage){
  393. return mtec.CountAverage;
  394. }else{
  395. return class{
  396. private count = 0;
  397. private _average: number;
  398. constructor(init?: number){
  399. if(init) this.add(init);
  400. }
  401. public get average(){
  402. return this._average ?? 0;
  403. }
  404. public add(value: number){
  405. this.count++;
  406. this._average = this.average + (value - this.average) / this.count;
  407. return this._average;
  408. }
  409. public clean(){
  410. this.count = 0;
  411. this._average = 0;
  412. }
  413. }
  414. }
  415. })())();
  416. }