欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

从RxJs到函数式编程,观察者模式,迭代器模式

程序员文章站 2022-06-20 18:32:33
从rxjs到函数式,观察者模式,迭代器模式。 rxjs 定义 rxjs 是一个库,它通过使用 observable 序列来编写异步和基于事件的程序。它提供了一个核心类...

从rxjs到函数式,观察者模式,迭代器模式

rxjs 定义

rxjs 是一个库,它通过使用 observable 序列来编写异步和基于事件的程序。它提供了一个核心类型 observable,附属类型 (observer、 schedulers、 subjects) 和受 [array#extras] 启发的操作符 (map、filter、reduce、every, 等等),这些数组操作符可以把异步事件作为集合来处理。

reactivex 结合了 观察者模式迭代器模式使用集合的函数式编程,以满足以一种理想方式来管理事件序列所需要的一切。

以上是来自官方的定义,这也是我为什么从这三个方面着手来了解rxjs。

函数式编程

函数式编程的总结可以参考阮大神的文章:函数式编程初探

函数为一等公民 (first class)

函数与其他数据类型一样,处于平等地位,可以赋值给其他变量,也可以作为参数,传入另一个函数,或者作为其它函数的返回值。

javascript闭包天然适合这种模式(java, c#做不到函数作为参数或返回值)

纯净性 (purity)

给定相同的输入参数,总是返回相同的结果 (没有math.random()这种) 没有产生任何副作用 没有依赖外部变量的值

不修改状态 - 利用参数保存状态
我觉得理解这句话有一个要点:在其他类型的语言中,变量往往用来保存”状态”(state)。不修改变量,意味着状态不能保存在变量中。函数式编程使用参数保存状态,最好的例子就是递归。

 function reverse(string) {
    if(string.length == 0) {
      return string;
    } else {
      return reverse(string.substring(1, string.length)) + string.substring(0, 1);
    }
  }

我认为这也是保持纯净性的一种措施。

rxjs中operator的定义和使用可以很好的体现出rxjs的函数式编程本质:

操作符是 observable 类型上的方法,比如 .map(…)、.filter(…)、.merge(…),等等。当操作符被调用时,它们不会改变已经存在的 observable 实例。相反,它们返回一个新的 observable ,它的 subscription 逻辑基于第一个 observable 。

操作符是函数,它基于当前的 observable 创建一个新的 observable。这是一个无副作用的操作:前面的 observable 保持不变。

操作符本质上是一个纯函数 (pure function),它接收一个 observable 作为输入,并生成一个新的 observable 作为输出。订阅输出 observalbe 同样会订阅输入 observable 。

在了解rxjs使用的设计模式之前,我们先看一下它的基本用法:

const search$ = rx.observable.fromevent(btn, 'click');  
search$.subscribe(function(event) {  
    //do something with event  
})

观察者模式 observable/observer

说到观察者模式,在rxjs之前,前端领域应用最广泛的莫过于dom事件监听了,我将它与rxjs observable作一个简单的类比:

从RxJs到函数式编程,观察者模式,迭代器模式

从观察者模式这个角度,rxjs和dom监听的本质是一样的。observer本质上就是对于事件(或状态变化)响应的handler,使一组原始数据变的observable就是为数据添加监听机制。

当然rxjs涵盖的scope更广,包括多值推送,状态管理(next, complete, error)以及函数式编程写法等等。

再举一个更加有代表性的栗子:

var observable = rx.observable.create(function (observer) {
  observer.next(1);
  observer.next(2);
  observer.next(3);
  settimeout(() => {
    observer.next(4);
    observer.complete();
  }, 1000);
});

console.log('just before subscribe');
observable.subscribe({
  next: x => console.log('got value ' + x),
  error: err => console.error('something wrong occurred: ' + err),
  complete: () => console.log('done'),
});
console.log('just after subscribe');
// 执行结果
//just before subscribe
//got value 1
//got value 2
//got value 3
//just after subscribe
//got value 4
//done

rx.observable.create时,并不会推送数据,这就是所谓惰性,当observavle发动subscribe并传入一个observer时,value 1, 2, 3被依次发射并被observer所接受,所谓多值推送。而依次推送,不就是一个有时间轴的stream(流)吗。

从RxJs到函数式编程,观察者模式,迭代器模式

在rxjs中,observable/observer的实现方式,可以参考如下代码:

class observable {
  constructor(_subscribe) {
    this._subscribe = _subscribe;
  }

  subscribe(observer) {
    return this._subscribe(observer);
  }
}

const simpleobservable = new observable((observer) => {
  let i = 0;
  const id = setinterval(() => {
    if (i < 3) {
      observer.next(i++);
    } else {
      observer.complete();
      observer.next('stop me!');
      clearinterval(id);
    } 
  }, 100);

 //返回取消订阅句柄
  return () => {
    console.log('disposed!');
    clearinterval(id);
  }
});

const observer = {
  next: value => console.log(`next -> ${value}`),
  error: () => {},
  complete: () => console.log('complete')
};

simpleobservable.subscribe(observer);

// 异步输出
// next -> 0
// next -> 1
// next -> 2
// complete

迭代器模式 interator

迭代器(iterator)模式。它提供一种方法顺序访问一个聚合对象中的各个元素,而又不需要暴露该对象的内部表示。迭代器模式可以把迭代的过程从业务逻辑中分离出来,在使用迭代器模式之后,即使不关心对象的内部构造,也可以按顺序访问其中的每个元素。

es6中,array、set 等这些内置的可迭代类型,可以通过 iterator 方法来获取一个迭代对象,调用迭代对象的 next 方法将获取一个元素对象。

let iterable = [1, 2];
let iterator = iterable[symbol.iterator]();
iterator.next(); // => { value: "1", done: false}
iterator.next(); // => { value: "2", done: false}
iterator.next(); // => { value: undefined, done: true}

在rxjs中在订阅一组可迭代原始类型时,内部是通过next方法来迭代的。

最后再题外说一点:

observablepromise的异同:

相同点:都是基于push的生产者消费者模式。
不同点
1. promise是即时单值推送,observable是惰性(lazy)多值推送。
2. observable可取消订阅,而promise不可以

注:关于生产者消费者中pull和push的描述,可参考以下链接:拉取 (pull) vs. 推送 (push)