web前端RxJS初步学习

来源:互联网 发布:网络管理视频教程下载 编辑:程序博客网 时间:2024/05/17 02:38
RxJS(Reactive Extensions for JavaScript) js响应式扩展


初学RxJS
http://cn.rx.js.org/manual/overview.html 中文网
https://zhuanlan.zhihu.com/p/23464709 demo网站
https://www.gitbook.com/book/buctwbzs/rxjs/details 比较好用初学者的网站


Observable (可观察对象): 表示一个概念,这个概念是一个可调用的未来值或事件的集合。(核心类型)
Observer (观察者): 一个回调函数的集合,它知道如何去监听由 Observable 提供的值。
Subscription (订阅): 表示 Observable 的执行,主要用于取消 Observable 的执行。
Operators (操作符): 采用函数式编程风格的纯函数 (pure function),使用像 map、filter、concat、flatMap 等这样的操作符来处理集合。
Subject (主体): 相当于 EventEmitter,并且是将值或事件多路推送给多个 Observer 的唯一方式。
Schedulers (调度器): 用来控制并发并且是中央集权的调度员,允许我们在发生计算时进行协调,例如 setTimeout 或 requestAnimationFrame 或其他


原生写法
var button = document.querySelector('button');
button.addEventListener('click', () => console.log('Clicked!'));//每次点击都会出现一个Clicked!
Rx写法
var button = document.querySelector('button');
Rx.Observable.fromEvent(button, 'click')
  .subscribe(() => console.log('Clicked!'))//每次点击都会出现一个Clicked!  事件触发.subscribe


原生写法
var count = 0; //全局变量可能引起代码污染(变量冲突)
var button = document.querySelector('button');
button.addEventListener('click', () => console.log(`Clicked ${++count} times`));
Rx写法   //纯净性 (Purity)
var button = document.querySelector('button');
Rx.Observable.fromEvent(button, 'click')
  .scan(count => count + 1, 0) //类似与reduce 获取上次的count 继续进行下一次操作 默认值0     
  .subscribe(count => console.log(`Clicked ${count} times`));


原生写法 一秒钟最多点一次
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} times`);
    lastClick = Date.now();//点击时记录时间作为下一次点击时判断时间
  }
});
Rx写法(RxJS 提供了一整套操作符来帮助你控制事件如何流经 observables ) //流动性 (Flow)
var button = document.querySelector('button');
Rx.Observable.fromEvent(button, 'click')
  .throttleTime(1000)//一秒只能点击一次
  .scan(count => count + 1, 0)//每次回调函数运行后的返回值会作为下次回调函数运行时的参数。
  .subscribe(count => console.log(`Clicked ${count} times`)); //点击时触发


原生写法 (获取值) //限制每秒只能点击一次并且 计算conut的值
var count = 0;
var rate = 1000;
var lastClick = Date.now() - rate;
var button = document.querySelector('button');
button.addEventListener('click', (event) => {
  if (Date.now() - lastClick >= rate) {
    count += event.clientX;
    console.log(count)
    lastClick = Date.now();
  }
});
Rx写法
var button = document.querySelector('button');
Rx.Observable.fromEvent(button, 'click')
  .throttleTime(1000) //限制每秒一次
  .map(event => event.clientX) //计算值
  .scan((count, clientX) => count + clientX, 0)
  .subscribe(count => console.log(count));


func.call() 意思是 "同步地给我一个值"
observable.subscribe() 意思是 "给我任意数量的值,无论是同步还是异步"


创建 Observables   Rx.Observable.create 
var observable = Rx.Observable.create(function subscribe(observer) {
  var id = setInterval(() => {
    observer.next('hi')
  }, 1000);
});
订阅 Observables
observable.subscribe(x => console.log(x));
执行 Observables
"Next" 通知: 发送一个值,比如数字、字符串、对象,等等。
"Error" 通知: 发送一个 JavaScript 错误 或 异常。
"Complete" 通知: 不再发送任何值。
清理 Observables
Subscription 表示进行中的执行,它有最小化的 API 以允许你取消执行。想了解更多订阅相关的内容,
请参见 Subscription 类型。使用 subscription.unsubscribe() 你可以取消进行中的执行:
var observable = Rx.Observable.from([10, 20, 30]);
var subscription = observable.subscribe(x => console.log(x));
// 稍后:
subscription.unsubscribe();


Subject (主体)多播 
它允许将值多播给多个观察者,所以 Subject 是多播的,而普通的 Observables 是单播的(每个已订阅的观察者都拥有 Observable 的独立执行)。


在下面的示例中,我们为 Subject 添加了两个观察者,然后给 Subject 提供一些值:
var subject = new Rx.Subject();
subject.subscribe({
  next: (v) => console.log('observerA: ' + v)
});
subject.subscribe({
  next: (v) => console.log('observerB: ' + v)
});
subject.next(1);
subject.next(2);
observerA: 1
observerB: 1
observerA: 2
observerB: 2


因为 Subject 是观察者,这也就在意味着你可以把 Subject 作为参数传给任何 Observable 的 subscribe 方法,如下面的示例所展示的:
var subject = new Rx.Subject();
subject.subscribe({
  next: (v) => console.log('observerA: ' + v)
});
subject.subscribe({
  next: (v) => console.log('observerB: ' + v)
});
var observable = Rx.Observable.from([1, 2, 3]);
observable.subscribe(subject); // 你可以提供一个 Subject 进行订阅
observerA: 1
observerB: 1
observerA: 2
observerB: 2
observerA: 3
observerB: 3


引用计数


手动调用 connect() 并处理 Subscription 通常太笨重。通常,当第一个观察者到达时我们想要自动地连接,
而当最后一个观察者取消订阅时我们想要自动地取消共享执行。
请考虑以下示例,下面的列表概述了 Subscriptions 发生的经过:
第一个观察者订阅了多播 Observable
多播 Observable 已连接
next 值 0 发送给第一个观察者
第二个观察者订阅了多播 Observable
next 值 1 发送给第一个观察者
next 值 1 发送给第二个观察者
第一个观察者取消了多播 Observable 的订阅
next 值 2 发送给第二个观察者
第二个观察者取消了多播 Observable 的订阅
多播 Observable 的连接已中断(底层进行的操作是取消订阅)
var source = Rx.Observable.interval(500);
var subject = new Rx.Subject();
var multicasted = source.multicast(subject);
var subscription1, subscription2, subscriptionConnect;


subscription1 = multicasted.subscribe({
  next: (v) => console.log('observerA: ' + v)
});
// 这里我们应该调用 `connect()`,因为 `multicasted` 的第一个
// 订阅者关心消费值
subscriptionConnect = multicasted.connect();//第一个观察者到达时连接
setTimeout(() => {
  subscription2 = multicasted.subscribe({
    next: (v) => console.log('observerB: ' + v)
  });
}, 600);
setTimeout(() => {
  subscription1.unsubscribe();
}, 1200);
// 这里我们应该取消共享的 Observable 执行的订阅,
// 因为此后 `multicasted` 将不再有订阅者
setTimeout(() => {
  subscription2.unsubscribe();
  subscriptionConnect.unsubscribe(); // 用于共享的 Observable 执行
}, 2000);


如果不想显示调用 connect(),我们可以使用 ConnectableObservable 的 refCount() 方法(引用计数),这个方法返回 Observable,
这个 Observable 会追踪有多少个订阅者。当订阅者的数量从0变成1,它会调用 connect() 以开启共享的执行。当订阅者数量从1变成0时,
它会完全取消订阅,停止进一步的执行。
refCount 的作用是,当有第一个订阅者时,多播 Observable 会自动地启动执行,而当最后一个订阅者离开时,多播 Observable 会自动地停止执行。
var source = Rx.Observable.interval(500);
var subject = new Rx.Subject();
var refCounted = source.multicast(subject).refCount();
var subscription1, subscription2, subscriptionConnect;
// 这里其实调用了 `connect()`,
// 因为 `refCounted` 有了第一个订阅者
console.log('observerA subscribed');
subscription1 = refCounted.subscribe({
  next: (v) => console.log('observerA: ' + v)
});
setTimeout(() => {
  console.log('observerB subscribed');
  subscription2 = refCounted.subscribe({
    next: (v) => console.log('observerB: ' + v)
  });
}, 600);
setTimeout(() => {
  console.log('observerA unsubscribed');
  subscription1.unsubscribe();
}, 1200);
// 这里共享的 Observable 执行会停止,
// 因为此后 `refCounted` 将不再有订阅者
setTimeout(() => {
  console.log('observerB unsubscribed');
  subscription2.unsubscribe();
}, 2000);
observerA subscribed//刚进入就被调用
observerA: 0//500ms
observerB subscribed//1000ms
observerA: 1//1000ms
observerB: 1/1000ms
observerA unsubscribed//1500ms
observerB: 2//1500ms
observerB unsubscribed//2000ms




Subject 的其中一个变体就是 BehaviorSubject,它有一个“当前值”的概念。它保存了发送给消费者的最新值。
并且当有新的观察者订阅时,会立即从 BehaviorSubject 那接收到“当前值”。
在下面的示例中,BehaviorSubject 使用值0进行初始化,当第一个观察者订阅时会得到0。第二个观察者订阅时会得到值2,尽管它是在值2发送之后订阅的。
var subject = new Rx.BehaviorSubject(0); // 0是初始值
subject.subscribe({
  next: (v) => console.log('observerA: ' + v)
});
subject.next(1);
subject.next(2);


subject.subscribe({
  next: (v) => console.log('observerB: ' + v)
});
subject.next(3);
observerA: 0
observerA: 1
observerA: 2
observerB: 2
observerA: 3
observerB: 3


ReplaySubject 记录 Observable 执行中的多个值并将其回放给新的订阅者。
当创建 ReplaySubject 时,你可以指定回放多少个值:
var subject = new Rx.ReplaySubject(3); // 为新的订阅者缓冲3个值
subject.subscribe({
  next: (v) => console.log('observerA: ' + v)
});
subject.next(1);
subject.next(2);
subject.next(3);
subject.next(4);


subject.subscribe({
  next: (v) => console.log('observerB: ' + v)
});
subject.next(5);
observerA: 1
observerA: 2
observerA: 3
observerA: 4
observerB: 2
observerB: 3
observerB: 4// 2 3 4 缓冲的三个值
observerA: 5
observerB: 5




Scheduler (调度器)
什么是调度器? - 调度器控制着何时启动 subscription 和何时发送通知。它由三部分组成:


调度器是一种数据结构。 它知道如何根据优先级或其他标准来存储任务和将任务进行排序。
调度器是执行上下文。 它表示在何时何地执行任务(举例来说,立即的,或另一种回调函数机制(比如 setTimeout 或 process.nextTick),或动画帧)。
调度器有一个(虚拟的)时钟。 调度器功能通过它的 getter 方法 now() 提供了“时间”的概念。在具体调度器上安排的任务将严格遵循该时钟所表示的时间。


var observable = Rx.Observable.create(function (observer) {
  observer.next(1);
  observer.next(2);
  observer.next(3);
  observer.complete();
})
.observeOn(Rx.Scheduler.async);
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
just after subscribe
got value 1
got value 2
got value 3
done
async 调度器操作符使用了 setTimeout 或 setInterval,即使给定的延迟时间为0。

原创粉丝点击