[Javascript] Broadcaster + Operator + Listener pattern -- 10. Define a Function to Set Common Behaviors in Operators
In our previous code, we have seen this partten for operators:
// #region operators const concat = curry((broadcaster, listener) => { let string = ''; return broadcaster((value) => { if (value === done) { listener(done); return; } listener((string += value)); }); }); const map = curry((transform, broadcaster, listener) => { return broadcaster((value) => { if (value === done) { listener(done); return; } listener(transform(value)); }); }); const filter = curry((predicator, broadcaster, listener) => { return broadcaster((value) => { if (value === done) { listener(done); return; } if (predicator(value)) { listener(value); } }); }); // #endregion
We can create a function to reduce the code:
const createOperator = curry((operator, broadcaster, listener) => {
})
let's say, the new function is called 'createOperator', it takes an operator, a broadcaster, and a istener.
The way we want to use it as:
const concat = createOperator((broadcaster, listener) => { let string = ''; return broadcaster((value) => { listener((string += value)); }); });
As you can notify, we remove the if condition.
Step1: Strict refoacting:
const createOperator = curry((operator, broadcaster, listener) => { return operator(broadcaster, listener) })
Now, it works almost the same as before, just without if condition check.
Step2: This time we want to create a new "broadcaster" and invoke the original broadcaster inside new broadcaster.
1. skeleton
const createOperator = curry((operator, broadcaster, listener) => { // new a new broadcaster and invoke original broadcaster inside new broadcaster return operator((behaviorListener) => { }, listener) })
So what is "behaviorListener": it actual refer to:
const concat = createOperator((broadcaster, listener) => { let string = ''; return broadcaster((value) => { listener((string += value)); }); });
2. invoke the original "broadcaster":
const _createOperator = curry((operator, broadcaster, listener) => { // new a new broadcaster and invoke original broadcaster inside new broadcaster return operator((behaviorListener) => { return broadcaster((value) => { behaviorListener(value) }) }, listener) })
We want to pass the value to "behaviorListener" which refer to the highlighted code.
Step3: Add common code:
const _createOperator = curry((operator, broadcaster, listener) => { // new a new broadcaster and invoke original broadcaster inside new broadcaster return operator((behaviorListener) => { // override the default broadcaster return broadcaster((value) => { // apply common logic if (value === done) { // stop outer listen to continue emitting values listener(done) return } behaviorListener(value) }) }, listener) })
We call "listener(done)" in order to stop the source futhur emtting the values.
---
Put all together:
const createOperator = curry((operator, broadcaster, listener) => { // new a new broadcaster and invoke original broadcaster inside new broadcaster return operator((behaviorListener) => { // override the default broadcaster return broadcaster(value => { // apply common logic if(value === done) { // stop outer listen to continue emitting values listener(done) return } // otherwise, we want to pass forward the value to listener behaviorListener(value) }) }, listener) })
Step4: Refactoring operators:
const concat = createOperator((broadcaster, listener) => { let string = ''; return broadcaster((value) => { listener((string += value)); }); }); const map = transform => createOperator((broadcaster, listener) => { return broadcaster((value) => { listener(transform(value)); }); }); const filter = predicator => createOperator((broadcaster, listener) => { return broadcaster((value) => { if (predicator(value)) { listener(value); } }); });
-- working code example --
import { curry, compose, toUpper, pipe } from 'ramda'; // #region listeners const _log = (value) => console.log(value); // #endregion // #region broadcasters const done = Symbol('done'); const addListener = curry((element, eventType, listener) => { return element.addEventListener(evenType, listener); }); const createInterval = curry((time, listener) => { let i = 0; const id = setInterval(() => { listener(i++); }, time); return () => { clearInterval(id); }; }); const createForOf = curry((iterator, listener) => { const id = setTimeout(() => { for (let item of iterator) { listener(item); } listener(done); }, 0); return () => { clearTimeout(id); }; }); const createZipOf = curry((broadcaster1, broadcaster2, listener) => { let cancelBoth; let buffer1 = []; const cancel1 = broadcaster1((value) => { buffer1.push(value); if (buffer2.length) { listener([buffer1.shift(), buffer2.shift()]); if (buffer1[0] === done || buffer2[0] === done) { listener(done); cancelBoth(); } } }); let buffer2 = []; const cancel2 = broadcaster2((value) => { buffer2.push(value); if (buffer1.length) { listener([buffer1.shift(), buffer2.shift()]); if (buffer1[0] === done || buffer2[0] === done) { listener(done); cancelBoth(); } } }); cancelBoth = () => { cancel1(); cancel2(); }; return cancelBoth; }); // #endregion // #region operators const createOperator = curry((operator, broadcaster, listener) => { // new a new broadcaster and invoke original broadcaster inside new broadcaster return operator((behaviorListener) => { // override the default broadcaster return broadcaster(value => { // apply common logic if(value === done) { // stop outer listen to continue emitting values listener(done) return } // otherwise, we want to pass forward the value to listener behaviorListener(value) }) }, listener) }) const concat = createOperator((broadcaster, listener) => { let string = ''; return broadcaster((value) => { listener((string += value)); }); }); const map = transform => createOperator((broadcaster, listener) => { return broadcaster((value) => { listener(transform(value)); }); }); const filter = predicator => createOperator((broadcaster, listener) => { return broadcaster((value) => { if (predicator(value)) { listener(value); } }); }); // #endregion const transform = pipe( map((x) => x[1]), filter((x) => x !== ','), concat, map(toUpper) ); let typeGreeting = transform( createZipOf(createInterval(100), createForOf('My Zipo')) ); const cancelGreating = typeGreeting(_log) // cancelGreating() const myZip = (broadcaster1, broadcaster2) => (...operators) => { return pipe(...operators)(createZipOf(broadcaster2, broadcaster2)) }
相关文章
- JavaScript 行内 | 内嵌 | 外链
- WordPress 教程:如何正确引用 JavaScript 和 CSS 文件
- Javascript下aes加解密详解编程语言
- JavaScript学习总结(二十)——Javascript非构造函数的继承详解编程语言
- JavaScript学习总结(十六)——Javascript闭包(Closure)详解编程语言
- 功能熟悉Oracle的SET功能,提高工作效率(oracle的set)
- Linux中的Set命令:配置为自由的系统(linux中的set命令)
- MySQL中如何设置变量使用SET语句(mysql 中set变量)
- 深入解析MySQL中SET作用域,优化数据库性能(mysql中set作用域)
- 优化优化Redis集合Set的性能(redis集合set性能)
- 从Redis中获取Set集合的简单方法(redis获取set集合)
- javascript实现的文字加密解密
- javascript-TreeView父子联动效果保持节点状态一致
- javascript数据结构的题
- javascript扫雷游戏
- JavaScript验证浏览器是否支持javascript的方法小结
- JavaScript使用简略语法创建对象的代码
- JavaScript设计模式富有表现力的Javascript(一)
- 在javascript中对于DOM的加强
- 输入自动提示搜索提示功能的javascript:sugggestion.js
- java与javascript之间json格式数据互转介绍
- 使用apply方法实现javascript中的对象继承
- javascript学习笔记之10个原生技巧