函数式响应式编程 - Functional Reactive Programming
前端  /  管理员 发布于 6年前   355
我们略过概念,直接看函数式响应式编程解决了什么问题。
故事从下面这个例子展开:
两个密码输入框,一个提交按钮。
密码、确认密码都填写并一致,允许提交;不一致提示错误。
html 如下:
<input id="pwd" placeholder="输入密码" type="password" /><br /><input id="confirmPwd" placeholder="再次确认" type="password" /><label id="errorLabel"></label><br /><button id="submitBtn" disabled>提交</button>
const validate = () => { const match = pwd.value === confirmPwd.value; const canSubmit = pwd.value && match; errorLabel.innerText = match ? "" : "密码不一致"; if (canSubmit) { submitBtn.removeAttribute("disabled"); } else { submitBtn.setAttribute("disabled", true); }};pwd.addEventListener("input", validate);confirmPwd.addEventListener("input", validate);
加强版问题: 输入密码时,确认密码还是空的,出现密码不一致错误提示,干扰用户输入。为解决这个问题,用 isConfirmPwdTouched 标识确认密码输入框是否输入过内容。
let isConfirmPwdTouched = false;pwd.addEventListener("input", () => { if (isConfirmPwdTouched) validate();});confirmPwd.addEventListener("input", () => { isConfirmPwdTouched = true; validate();});
测试同学又发现了一个 bug:
不输密码,直接输入确认密码,这时又出现了错误提示。
为解决这个问题,再加入一个标识位 isPwdTouched。
let isConfirmPwdTouched = false;let isPwdTouched = false;pwd.addEventListener("input", () => { isPwdTouched = true; if (isPwdTouched && isConfirmPwdTouched) validate();});confirmPwd.addEventListener("input", () => { isConfirmPwdTouched = true; if (isPwdTouched && isConfirmPwdTouched) validate();});
旗舰版问题: 确认密码输入框输入第一个字符时就会提示密码不一致,干扰用户输入。为解决这个问题,高级一点的做法是使用高阶函数 debounce,否则又要多个标识位。
const debounce = (fn, ms) => { let timeoutId; return (...args) => { if (timeoutId !== undefined) clearTimeout(timeoutId); timeoutId = setTimeout(fn.bind(null, ...args), ms); };};const validate = () => { const match = pwd.value === confirmPwd.value; const canSubmit = pwd.value && match; errorLabel.innerText = match ? "" : "密码不一致"; if (canSubmit) { submitBtn.removeAttribute("disabled"); } else { submitBtn.setAttribute("disabled", true); }};const debouncedValidate = debounce(validate, 200);let isConfirmPwdTouched = false;let isPwdTouched = false;pwd.addEventListener("input", () => { isPwdTouched = true; if (isPwdTouched && isConfirmPwdTouched) debouncedValidate();});confirmPwd.addEventListener("input", () => { isConfirmPwdTouched = true; if (isPwdTouched && isConfirmPwdTouched) debouncedValidate();});
可以看出:随着交互越来越复杂,常规做法的标识位越来越多,代码的逻辑越来越难理清。
常规做法实际实现了下图的逻辑:
图看起来清晰易懂,但可惜的是 代码和这张图长得并不像。
有没有一种办法,让我们的代码和上图一样逻辑清晰呢?
答案就是:函数式响应式编程。用它写代码就像是在画上面那张图。
这里使用的库是rxjs。
const { fromEvent, combineLatest } = rxjs;const { map, debounceTime } = rxjs.operators;const pwd$ = fromEvent(pwd, "input").pipe(map(e => e.target.value));const confirmPwd$ = fromEvent(confirmPwd, "input").pipe( map(e => e.target.value));combineLatest(pwd$, confirmPwd$) .pipe( debounceTime(200), map(([pwd, confirmPwd]) => ({ match: pwd === confirmPwd, canSubmit: pwd && pwd === confirmPwd })) ) .subscribe(({ match, canSubmit }) => { errorLabel.innerText = match ? "" : "密码不一致"; if (canSubmit) { submitBtn.removeAttribute("disabled"); } else { submitBtn.setAttribute("disabled", true); } });
没看出代码和上面那张图有什么相似?我们来拆解一下。
const pwd$ = fromEvent(pwd, "input").pipe(map(e => e.target.value));const confirmPwd$ = fromEvent(confirmPwd, "input").pipe( map(e => e.target.value));
我们把 pwd$, confirmPwd$ 称作流,可以把它们想象成河流,里面流淌着数据。
map 把流中的 input event 转换为输入框的 value。
combineLatest(pwd$, confirmPwd$);
combinLatest 的作用在这里有两个。
combine:把 pwd$, confirmPwd$ 合成一个新流latest:新流中的数据为 pwd$, confirmPwd$ 最新的数据的组合pwd$ 产生数据 a 时,confirmPwd$ 还没产生过数据,新流不产生数据;pwd$ 产生数据 ab 时,confirmPwd$ 还没产生过数据,新流不产生数据;confirmPwd$ 产生数据 a 时,combineLatest(pwd$, confirmPwd$).pipe( debounceTime(200), map(([pwd, confirmPwd]) => ({ match: pwd === confirmPwd, canSubmit: pwd && pwd === confirmPwd })));
debounceTime(200) 的作用和普通做法里的 debounce 功效一样。
上游流产生 [ab, a] 时,新流不立刻把数据传给下游,而是要延迟 200ms。200ms 不到,上游流又传来数据 [ab, ab],新流丢弃之前的数据。200ms 后,上游流没有传来新数据,新流将 [ab, ab] 传给下游。map 将 [ab, ab] 转化为 { match: true, canSubmit: true }。
再比较一下,是不是很像呢?
函数式响应式编程创造的初衷就是解决 listener callback 逻辑表达不直观,代码乱成一团麻 的问题。至于它为什么叫函数式响应式编程,是因为它的实现借鉴了函数式、响应式编程思想。
例如:
declarative122 在
学历:一种延缓就业设计,生活需求下的权衡之选中评论 工作几年后,报名考研了,到现在还没认真学习备考,迷茫中。作为一名北漂互联网打工人..123 在
Clash for Windows作者删库跑路了,github已404中评论 按理说只要你在国内,所有的流量进出都在监控范围内,不管你怎么隐藏也没用,想搞你分..原梓番博客 在
在Laravel框架中使用模型Model分表最简单的方法中评论 好久好久都没看友情链接申请了,今天刚看,已经添加。..博主 在
佛跳墙vpn软件不会用?上不了网?佛跳墙vpn常见问题以及解决办法中评论 @1111老铁这个不行了,可以看看近期评论的其他文章..1111 在
佛跳墙vpn软件不会用?上不了网?佛跳墙vpn常见问题以及解决办法中评论 网站不能打开,博主百忙中能否发个APP下载链接,佛跳墙或极光..
Copyright·© 2019 侯体宗版权所有·
粤ICP备20027696号