响应式编程 ---- 笔记
The introduction to Reactive Programming you've been missing | 【译】响应式编程介绍
从 "点击事件" 说起
dom.addEventListener("click", event => {console.log(event)});
这是一个典型的事件绑定, 而 事件绑定 又是一种典型的 "异步". 因此, 扯到这里, 就不得不将 "响应式编程" 和 "异步" 联系起来了. (看起来并不完全是这样, 在 Wikipedia 里有编辑者认为 响应式编程 并不完全是和 异步编程 一起出现的, 因为在单线程程序中依然可以使用 响应式编程 2)
从这个例子出发, 我们提取出来三个对象 (就像提取一个句子的语法成分一样):
- event (stream) 为什么是"事件流"而不是"dom"?
- addEventListener
- callback function
我们依次为上述三个对象设置一个神乎的概念: 可观察对象(observable), 订阅(subscribing), 观察者( observers)
而一个 "(事件)流" 则由一个具体的对象, 例如 事件/ 数据/ 用户的输入等等, 经包装后产生.
直观上来讲, 从代码中看起来 addEventListener
是一个作用于 dom
的动作, 因此 dom
才更像是这个"绑定"动作的主体. 但是对于 "响应式编程" 而言, 一个 可观察对象 应当是 "随时间推移可发生变化的". 在这里, dom
并不会发生变化 (或者说即使它发生了变化也不会使示例中的 回调函数 执行), 故它并不是本次示例的 可观察对象.
对 "流" 附加操作
在上文中我们说, 一个 "流" 是可以随着时间推移发生变化的, 而 "流" 的每次变化, 最终都将传递到 "观察者" , 观察者据此将会做出预设的动作. 那么, "变化" 在传递到观察者的过程中, 我们是否可以对它做一些操作使它到达观察者时更符合我们的预期效果呢? 当然可以. 我们只需要对 "流" 做一些附加的操作就可以了.
可以想象一条流动的"水流", 水流的源头是事件发生的地方("事件源"), 下游是我们依河而建("绑定")的机器, 机器一旦有水流流入便会做一些预设的"动作". 而在水流流入机器的途中, 我们依然可以设置一层层的装置来处理水流以使其到达下游的机器时能够符合我们的预设要求. 这个过程中我们还可以看到以下几点:
- 每次水源处有水流出都会经过处理装置处理(并最终可能会到达下游的机器);
- 水流每经过一层 处理装置 都会将处理结果"累积"到下一层
当然, 不可否认的是, 上述比喻实际上并非完全正确的, 对于 "处理水流" 这个比喻, 它在效果上可以说是正确的但是从本质上确实错误的. 因为在实际的 响应式编程 中, 每个附加在 流 之上的 操作 都不会修改原来的 流, 取而代之的是, 它会创建一个新的流, 这个流则是在源事件流基础上由该操作处理后的事件流, 这里提现了函数式编程的原则之一: "不变性" (immutability)
那么, 可以为 "流" 附加哪些操作呢? 这应当是取决于框架实现的(我猜), 例如 Rx 框架里有如下的操作:
// TODO
再究 "响应式"
对于 "响应式编程" 与传统的编程范式的对比, Wikipedia 里有如下一段表述:
For example, in an imperative programming setting,
a := b + c would mean thata is being assigned the result ofb+c in the instant the expression is evaluated, and later, the values ofb andc can be changed with no effect on the value ofa . On the other hand, in reactive programming, the value ofa is automatically updated whenever the values ofb orc change, without the program having to re-execute the statementa:=b+c to determine the presently assigned value ofa [citation needed]Another example is a hardware description language such as Verilog, where reactive programming enables changes to be modeled as they propagate through circuits.[citation needed]
看到
wire [3:0] t2,t3,t4;
wire [3:0] d1,d2,d3,d4;
assign d4 = {1'b0, hex[5:3]};
assign d3 = {t4[2:0], hex[2]};
assign d2 = {t3[2:0], hex[1]};
assign d1 = {t2[2:0], hex[0]};
我们将 t2, t3, t4, d1, d2, d3, d4 几条数据排线连接在一起, 那么只要有一根线的电平发生了变化, 那么所有与之相连的线路都将会发生变化. 不仅如此, 我们还可以使用 always
声明一个 "事件", 只要该事件(例如一个"下降沿")发生了就会执行我们预设的动作. 此外, 如果我们给某一条数据排线串接一组门电路的话, 还可以在这条数据排线的数据到达下一个组件前按需处理这些数据.
// TODO 实操文首的博客中的实例, 再记录.