随手写了点代码
发布于 18 小时前 作者 dou4cc 88 次浏览 来自 分享
<!DOCTYPE html>
<html>

<head>
	<meta charset="utf-8">
</head>

<body>
	<script>
		"use strict";

		const multi_key_map = () => {
			const tree = new Map;
			const symbol = Symbol();
			return {
				set: (...args) => {
					const keys = [...args.slice(0, -1), symbol];
					const [value] = args.slice(-1);
					const f0 = (parent, ...keys) => {
						if(keys.length > 1){
							if(parent.has(keys[0]) && f0(parent.get(keys[0]), ...keys.slice(1)) === 0) parent.delete(keys[0]);
						}else{
							parent.delete(keys[0]);
						}
						return parent.size;
					};
					const f1 = (parent, ...keys) => {
						if(keys.length > 1){
							if(!parent.has(keys[0])) parent.set(keys[0], new Map);
							f1(parent.get(keys[0]), ...keys.slice(1));
						}else{
							parent.set(keys[0], value);
						}
					};
					(value === undefined ? f0 : f1)(tree, ...keys);
				},
				get: (...args) => {
					const f = (parent, ...keys) => parent && keys.length > 0 ? f(parent.get(keys[0]), ...keys.slice(1)) : parent;
					return f(tree, ...args, symbol);
				},
			};
		};

		const tube = (() => {
			const dp = new WeakMap;
			return source => dp.get(source) || (() => {
				const storage = multi_key_map();
				const tube0 = (...condition) => {
					const sync = () => {
						state = storage.get(...condition);
						if(state){
							state.handle_num += 1;
						}else{
							state = {
								alive: true,
								thunk: (() => {
									const thunk = source(...condition);
									return thunk ? listener => thunk(listener) : thunk;
								})(),
								handle_num: 1,
								listener_num: 0,
								cache: new Set,
							};
							storage.set(...condition, state);
						}
					};
					const unsync = () => {
						state.handle_num -= 1;
						if(state.handle_num === 0){
							state.alive = false;
							storage.set(...condition, undefined);
							(state.thunk || (() => {}))();
						}
					};
					const listen = listener => {
						const f0 = listener => {
							const update = () => {
								if(!lock){
									lock = true;
									Promise.all([(async () => {
										await (cancel || (() => {}))();
										cancel = listener(...solution);
									})(), new Promise(resolve => setTimeout(resolve, 0))]).then(() => {
										lock = false;
									});
								}
							};
							let lock = false;
							let cancel;
							return [
								(...solution1) => {
									const f = (a, b) => a.length === 0 || a[0] === b[0] && f(a.slice(1), b.slice(1));
									if(!solution || solution.length !== solution1.length || !f(solution, solution1)){
										solution = solution1;
										update();
									}
								},
								() => {
									listener = () => {};
									if(solution) update();
								},
							];
						};
						const f1 = () => {
							let used = false;
							return () => {
								if(!used){
									used = true;
									cancel();
									const listen = listener1 => {
										clearTimeout(timer);
										state.cache.delete(listen);
										[listener, cancel] = f0(listener1);
										if(solution) listener(...solution);
										return f1();
									};
									state.cache.add(listen);
									const timer = setTimeout(() => {
										state.cache.delete(listen);
										listener_num -= 1;
										state.listener_num -= 1;
										if(listener_num === 0) unsync();
										(thunk || (() => {}))();
									}, 0);
								}
							};
						};
						used = true;
						listener_num += 1;
						state.listener_num += 1;
						let solution;
						let cancel;
						[listener, cancel] = f0(listener);
						const thunk = state.thunk((...solution) => {
							listener(...solution);
						});
						return f1();
					};
					let state;
					let used = false;
					let listener_num = 0;
					sync();
					return listener => {
						if(listener){
							if(!state || !state.alive) sync();
							if(state.listener_num > 0){
								for(let thunk of state.cache) return thunk(listener);
								let timer;
								const promise = new Promise(resolve => {
									timer = setTimeout(() => {
										timer = null;
										for(let thunk of state.cache) return resolve(thunk(listener));
										resolve(listen(listener));
									}, 0);
								});
								return () => {
									if(timer){
										clearTimeout(timer);
									}else{
										promise.then(thunk => thunk());
									}
								};
							}
							return listen(listener);
						}
						if(!used){
							used = true;
							setTimeout(() => {
								if(listener_num === 0) unsync();
							}, 0);
						}
					};
				};
				dp.set(tube0, tube0);
				dp.set(source, tube0);
				return tube0;
			})();
		})();

		//以下是页面逻辑:
		const mouse_pos = tube(() => {
			const listener_set = new Set;
			const onmousemove = ({screenX, screenY}) => listener_set.forEach(listener => listener(screenX, screenY));
			addEventListener("mousemove", onmousemove);
			return listener0 => {
				if(listener0){
					const listener = (...args) => listener0(...args);
					listener_set.add(listener);
					return () => listener_set.delete(listener);
				}
				removeEventListener("mousemove", onmousemove);
			};
		});
		const thunk = mouse_pos()((x, y) => {
			const text = document.createTextNode(x + ", " + y);
			document.body.append(text);
			return async () => {
				await new Promise(resolve => setTimeout(resolve, 200));
				text.remove();
			};
		});
		addEventListener("dblclick", () => thunk());
	</script>
</body>

</html>

请用Firefox最高版本运行。

回到顶部