响应式编程优点 有效_Rxjs—从命令式到响应式(一)

ffc8873edafe0692ad337d707188f88e.png

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中全局变量是魔鬼,我们不得不随时小心这些定时炸弹。

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

  1. 所有数据处理的过程使用操作符的全部都是纯函数,它们只是单纯的接收输入,产生输出,并不会对输入的值做出任何改变。
  2. 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中如何去应用响应式,个人认为写代码的过程其实不难,难的是思维方式的转换,希望能从文字的描述上给大家一点启发。

Angular完全开发手册​www.hijavascript.com
057576b286a8f2b8c926c20d1ae56036.png
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值