资讯专栏INFORMATION COLUMN

从命令式到响应式(一)

JayChen / 2631人阅读

摘要:响应式命令式这两种编程风格的思维方式是完全相反的。第二种方式是工人主动去找工人索取生产手机所要的零件,然后生产一台完整的手机,这两种方式就对应的响应式和命令式。

angular2中内置了rxjs,虽然框架本身并没有强制开发者使用响应式风格来组织代码,但是从框架开发团队的角度可以看出他们必然是认同这种编程风格的。rxjs本质是基于函数式编程的响应式风格的库,函数式相对于面向对象来说更加抽象,响应式的思维方式和命令式的思考方式又截然相反,所以导致大多数的开发者开始接触时觉得非常不适应,认为门槛太高,其实不然,只要思维方式能转变,你会觉得响应式是更加顺其自然的事情。响应式涉及的知识点很多,这篇文章只能做到管中窥豹,让大家在感性上对它有一个认识。

面向对象 vs 函数式

抽象层级不同:简单来说,面向对象抽象了物,而函数式抽象了行为。最简单的例子:

function Person(name, age) {
    this.name = name;
    this.age = age;
}

Person.prototype.ageAfterYears = function(years) {
    console.log(this.age + years);
}

var john = new Person("John", 18);

面向对象的方式

john.ageAfterYears(5) // 23;

函数式的方式:

function getAge(baseAge) {
    return (obj) => console.log(obj.age + baseAge);
}

const ageAfterYears = getAge(5);

ageAfterYears(john); // 23;

这段代码很简单,但足以说明问题,使用面向对象的方式时,我们直接调用person类的方法,计算出实例的年龄,我们会很在乎这个对象上是否提供这么一个方法。但使用函数式时,ageAfterYears函数并不在乎传入的对对象上是否有那个方法,只需要传入的对象有上age属性,它就可以得一个结果,我们更在乎的是这种行为是否正确,例如计算函数的行为是加,而不是减,或者其它。

响应式 vs 命令式

这两种编程风格的思维方式是完全相反的。假设一个生产手机的过程,第一种方式是工人 A 先处理零件,然后交给工人 B, 工人 B 接收后生产出一台手机。第二种方式是工人 B 主动去找工人 A 索取生产手机所要的零件,然后生产一台完整的手机,这两种方式就对应的响应式和命令式。可以看出,响应式更适合于流水线式的生产,因为它节省了大量的沟通成本。尤其重要的一点是工人 B的行为是‘懒’的,它不会主动的去争取任务,而是一直在等待任务的到来。在程序中,数据就是我们产品的零件,因此也适合这种流水线式的处理。假如我们需要控制用户在一个按钮上每秒最多只能点击一次,看下面这段代码:

使用命令式:

var count = 0;

var rate = 1000;

var lastClick = Date.now() - rate;

var button = document.querySelector("button");

button.addEventListener("click", () => {
    if(Date.now() - lastClick >= rate) {
        console.log(`Clicked ${++count} timers`);
        lastClick = Date.now();
    }
});

使用响应式:

var button = button.querySelector("button");

Rx.Observable.fromEvent(button, "click")
    .throttleTime(1000)
    .scan(count => count + 1, 0)
    .subscribe(count => console.log(`Click ${count} times`));

从代码上可以看出,命令式的代码量更大,而响应式的只有区区几行,当然你可以说这是因为使用了rxjs封装的库,但更为重要的是在命令式的代码中变量的数量更多,尤为糟糕的是这些变量相对于主逻辑来说处于‘全局‘的位置,而javascript中全局变量是魔鬼,我们不得不随时小心这些定时炸弹。

上面已经提到过响应式的处理过程是’懒‘的,除此之外,它的优点可以概括如下

所有数据处理的过程使用操作符的全部都是纯函数,它们只是单纯的接收输入,产生输出,并不会对输入的值做出任何改变。

rxjs实现的 Observable 是一个可以产生多个值序列,是一个 push 类型的系统,这和 pull 类型的函数系统有很大的不同。

push vs pull

这里使用 pull 和 push 来描述值的生产者和消费者之间是如何发生联系的,它们是两种完全不同的协议。

在pull的系统中,值的消费决定什么时间从生产者上获取数据,生产者本身并不关心数据什么时间分发给消费者。

每一个javascript函数都可以看作一个 pull 类型的系统。函数可以产生值,但这是通过调用者主动调用函数,函数运行产生出值返回给调用者的方式进行的,所以可以理解为调用者主动去函数上拉取了值。

ES2015中介绍另外两种 pull 类型的系统,generator函数 和 iterator。对于它们来讲,遍历器对象的 next 方法可以视作值的消费者,通过iterator.next()可以获取到多个值。

Producer Consumer
pull 被动:当被调用时产生值 主动:决定何时发起调用
push 主动:按自身的设置产生值 被动:响应接收到的值

在push的系统中,生产者决定什么时候发送值给消费者,消费者并不知道什么时候可以接收到值。

ES6中的 promise 就是一个非常典型的 push 系统,promise 将 resolve 的结果传递给注册在它内部的回调函数,这与普通的函数有很大的不同,回调函数何时可以接收到数据完全取决于 promise 什么时间向它传递数据。

rxjs的 Observable 也是一种 push 类型的系统,一个 Observable 可以产生多个值,然后推送给它的订阅者。

Function 是一个 ‘懒’ 的求值过程,只有在被调用时它才会同步的返回一个值给调用者。

generator 也是一个 ’懒‘ 的求值过种,在遍历的过程中同步的返回0个或多个值给调用者。

Promise 经过运算后可能产生一个值,当然也可能产生一个错误。

Observable 也是一个‘懒’的求值过程,当它被订阅后可以同步或者异步的产生出0个或者无限多个值给调用者,这个过程将一直持续到订阅被取消或者流结束。

今天先写到这里,没有过多的代码, 更没有涉及到angular中如何去应用响应式,个人认为写代码的过程其实不难,难的是思维方式的转换,希望能从文字的描述上给大家一点启发。

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/97265.html

相关文章

  • 命令响应(六)

    摘要:从这个系列的第一章开始到第五章,基于的响应式编程的基础知识基本上就介绍完了,当然有很多知识点没有提到,比如,等,不是他们不重要,而是碍于时间精力等原因没办法一一详细介绍。 从这个系列的第一章开始到第五章,基于rxjs的响应式编程的基础知识基本上就介绍完了,当然有很多知识点没有提到,比如 Scheduler, behaviorSubject,replaySubject等,不是他们不重要,...

    sanyang 评论0 收藏0
  • 命令响应(七)

    摘要:响应式组件上节中错误的把添加到了中,实际中它返回的是一个,先调整过来。将这两条流合并成一条请求流。利用上面实现的方法,可以很方便的拿到响应流,即。由于不同于,请求和响应一一对应的很好,所以这里可能需要一定的条件来拿到请求对应的响应。 上回搭建了一个组件以及它所依赖的服务的基本结构,这节接着它继续。另外从本节开始,统一采用rxjs6的风格,6和5在写法上最大的不同就是弃用链式调用,而采用...

    MorePainMoreGain 评论0 收藏0
  • 命令响应(八)

    摘要:最近有同学在使用时总是不能如愿拿到自己想要的数据,说到底还是没有能把思维从命令式的习惯中转换过来。响应式的编程风格中,数据应该在流内完成转换,合并,过滤,而不是取出来,一顿操作再丢回流里。 最近有同学在使用rxjs时总是不能如愿拿到自己想要的数据,说到底还是没有能把思维从命令式的习惯中转换过来。 Observable !== 异步 && Observable !== 同步 如题,请默念...

    canger 评论0 收藏0
  • 命令响应(三)

    摘要:它有三个方法,用来通知下一个值是什么方法在上产生错误时通知观察者,方法用来通知观察者当前流上的值已经发射完毕。除此之外在我们不需要订阅时,观察者还应该能够安全的退出。 上次说了响应式中使用的设计模式,今天我们主要来看rxjs中实现响应式的三大件,Observable, subject 和 Subscription; Observable--可观测序列 在第一节中介绍过 push 类型的...

    k00baa 评论0 收藏0
  • 翻译连载 | 第 10 章:异步的函数(下)-《JavaScript轻量级函数编程》 |《你不知

    摘要:而数组里则是为每一个值运行一次映射函数,无论这个值何时加入,然后把它返回到里。上一章翻译连载第章异步的函数式上轻量级函数式编程你不知道的姊妹篇原创新书移动前端高效开发实战已在亚马逊京东当当开售。 原文地址:Functional-Light-JS 原文作者:Kyle Simpson-《You-Dont-Know-JS》作者 关于译者:这是一个流淌着沪江血液的纯粹工程:认真,是 ...

    魏明 评论0 收藏0

发表评论

0条评论

最新活动
阅读需要支付1元查看
<