RxJS学习笔记

Observable和Observer

简介

Observable对象是一个发布者,Observer是一个观察者,它们通过Observable对象的subscribe函数关联起来。
在Observable和Observer两者的关系里,Observer处于被动地位,代码中可能并看不到一个Observer对象,而只看到分别代表next、error和complete的函数。

程墨. 深入浅出RxJS (实战)
观察者模式
数据是由Observable推送给Observerd

const observer ={
	next: item=>{表示现在要推送的数据},
	error:err=>{表示Observable的出错处理)} ,
	complete:()=>{表示Obervable的完结)}
}

订阅和退订

订阅 observable.subscribe(observer对象);
退订 unsubscribe();
代码如下所示:

const source$ = new Observable(创建方式);
const subscription = source$.subscribe( 
	item = > console.log( item)
	);
setTimeout(() = > { subscription.unsubscribe(); }, 3500);

Hot Observable 和 Cold Observable

Hot ~类似于打开电视切换频道,订阅时,错过了就错过了。
Cold ~ 类似于视频点播网站,订阅时,都是从这个视频的第一秒开始。

创建类操作符

创建类操作符即创建指定数据集合的Observable对象。

创建同步数据流

//create

//of 列举数据 吐出数据是同步的(没有任何时间间隔)
const source$ = Observable.of( 1, 2, 3);
//range指定范围 创建1到100的正整数 ,同步
const source$ = Observable.range( 1, 100);
//generate 循环创建 四个参数
const source$ = Observable.generate(
	2, //初始值,相当于for循环中的i=2
	value => value<10, //继续的条件,相当于for中的条件判断
	value => value+2, //每次值的递增
	value => value*value //产生的结果
);
//repeat:重复数据的数据流 内容重复10次 生成30个数据(工作机制:多次订阅和多次退订,只有上游数据退订之后才能进行下次订阅)
const source$ = Observable.of( 1, 2, 3); 
const repeated$ = source$.repeat( 10);
//empty就是产生一个直接完结的Observable对象,没有参数,不产生任何数据,直接完结,
const source$ = Observable.empty();
//throw产生的Observable对象也是什么都不做,直接出错,抛出的错误就是throw的参数,
const source$ = Observable.throw( new Error(' Oops'));
//never产生的Observable对象就真的是什么都不做,既不吐出数据,也不完结,也不产生错误,就这样待着,一直到永远。
const source$ = Observable.never();

创建异步数据的Observable对象

简单创建

//永不停止的数据流
const source$ = Observable.interval( 1000);
//支持一参 或 二参
const source$ = Observable.timer( 2000, 1000);
//from:可把一切转化为Observable 例:[1,2,3];迭代函数,‘abc’,Promise对象 等
const source$ = Observable.from([1,2,3]);
//repeatWhen 接收到上游完结事件的时候等待一段时间再重新订阅,接受一个函数
const source$ = Observable.of( 1, 2, 3);
const notifier = (notification$) = > {
	 //delay的作用就是把上游传下来的数据延时一段时间再转手给下游,最终结果是,每次repeatWhen的上游完结的时候,2000毫秒之后再重新订阅上游。
	return notification$.delay( 2000); }; 
const repeated$ = source $.repeatWhen( notifier);
/*
defer 延迟创建数据流
source$被创建出来的时候,并没有任何有实际数据的Observable产生,只有当它被订阅的时候,observableFactory就会被调用,这时候通过of产生的数据流才被创造出来作为真正的数据源。
*/
const observableFactory = () = > Observable.of( 1, 2, 3); 
const source$ = Observable.defer( observableFactory);

着重介绍fromEvent

/**
(1) fromEvent最常见的用法就是把DOM中的事件转化为Observable对象中的数据。
第一个参数是一个事件源,在浏览器中,最常见的事件源就是特定的DOM元素,第二个参数是事件的名称,对应DOM事件就是click、mousemove这样的字符串。
*/ 
const event$ = Rx.Observable.fromEvent( document.querySelector('#clickMe'), 'click');
/**
(2)fromEvent可以从Node.js的events中获得数据,
属于Hot Observable,数据的产生和订阅无关
*/
const emitter = new EventEmitter();
const source$ = Observable.fromEvent( emitter, 'msg'); 
//订阅
source$.subscribe( 
	console.log, 
	error = > console.log('catch', error), 
	() = > console.log('complete') 
	);
emitter.emit('msg', 1);

fromEventPattern

fromEvent能够从事件源产生Observable,但是要求数据源表现得像是浏览器的DOM	 
或者Node.js的EventEmitter,在某些情况下,事件源可能并不按照这样的方式产生数据,
对于这种情况,就需要用一个灵活度更高的操作符叫做fromEventPattern。		
fromEventPattern接受两个函数参数,分别对应产生的Observable对象被订阅和退订时的动作,
因为这两个参数是函数,具体的动作可以任意定义,
const emitter = new EventEmitter();
//定义订阅时的动作 
const addHandler = (handler) => { 
	emitter.addListener('msg', handler);
	};
//定义退订时的动作	
const removeHandler = (handler) =>{
	emitter.removeListener('msg', handler); 
} 

const source$ = Observable.fromEventPattern(addHandler,removeHandler); 
//订阅
const subscription = source$.subscribe( 
	console.log, 
	error = > console.log(' catch', error), 
	() = > console.log(' complete') 
	); 
emitter.emit('msg', 'hello'); //订阅之后输出 
emitter.emit('msg', 'world'); //输出
subscription.unsubscribe();  //退订
emitter.emit('msg', 'end'); //退订之后 不会输出

ajax

Rx.Observable.fromEvent( '按钮DOM', 'click' ).subscribe( 
() = > { 
	Rx.Observable.ajax('URL',{responseType: 'json'}).subscribe( value = > { '处理返回数据' });
	});

合并数据流

将多个Observable合并成一个对象
操作符

一阶合并

操作符合并解释
concat首尾相连;(图3-1)
merge先到先得,快速通过 (合并DOM事件);(同步限流)merge可以有一个可选参数concurrent,用于指定可以同时合并的Observable对象个数,超过这个个数的不会合并;(图3-2)
zip拉链式组合,(一对一合并,容易产生数据积压问题);(图3-3)
combinelLatest当任何一个上游Observable产生数据时,从所有输入Observable对象中拿最后一次产生的数据(最新数据),然后把这些数据组合起来传给下游。此外,最后一个参数(可选)可以是一个函数,这里我们称之为project,project的作用是让combineLatest把所有上游的“最新数据”扔给下游之前做一下组合处理(参考下 引用1);(图3-4)
withLatestFrom功能类似于combineLatest,但是给下游推送数据只能由一个上游Observable对象驱动。调用withLatestFrom的那个Observable对象起到主导数据产生节奏的作用,作为参数的Observable对象只能贡献数据,不能控制产生数据的时机。可用于减少不必要的网页渲染(图3-5)
race胜者通吃 >>第一个吐出数据的Observable对象就是胜者,race产生的Observable就会完全采用胜者Observable对象的数据,其余的输入Observable对象则会被退订而抛弃。图3-6)
startWith让一个Observable对象在被订阅的时候,总是先吐出指定的若干个数据,也就是说startWith产生的Observable对象,当被订阅的时候会立刻吐出startWith的参数
forkJoin只有当所有Observable对象都完结,确定不会有新的数据产生的时候,forkJoin就会把所有输入Observable对象产生的最后一个数据合并成给下游唯一的数据

withLatestFrom 只有实例操作符forkJoin 只有静态操作符以外,以上其他合并操作符均有静态和实例操作两种

引用1
const project=(a,b)=>${a} and ${b};
source1 . c o m b i n e L a t e s t ( s o u r c e 2 .combineLatest(source2 .combineLatest(source2,project);

图3-1
3-1

图3-2
3-2

图3-3
3-3

图3-4
3-4!

图3-5
3-5

高阶Observable

高阶Observable的合并操作符

  1. concatAll

  2. mergeAll

  3. zipAll

  4. combineAll 就是处理高阶Observable的combinelLatest

  5. switch

    总是切换到最新的内部Observable对象获取数据。每当switch的上游高阶Observable产生一个内部Observable对象,switch都会立刻订阅最新的内部Observable对象上,如果已经订阅了之前的内部Observable对象,就会退订那个过时的内部Observable对象,这个“用上新的,舍弃旧的”动作,就是切换。
    3.2-1

  6. exhaust 耗尽

在耗尽当前内部Observable的数据之前不会切换到下一个内部Observable对象。
3.2-2

辅助类操作符

操作符说明
countcount的作用是统计上游Observable对象吐出的所有数据个数。
max取得上游Observable吐出所有数据的“最大值”。
min取得“最小值”。
reduce规约。
erery要求一个判定函数作为参数,上游Observable吐出的每一个数据都会被这个判定函数检验,如果所有数据的判定结果都是true。反之,只要上游吐出的数据中有一个数据检验为false,那么也不用等到上游Observable完结,every产生的Observable对象就会立刻吐出false
find找到上游Observable对象中满足判定条件的第一个数据,产生的Observable对象在吐出数据之后会立刻完结,两者不同之处是,find会吐出找到的上游数据
findIndex同find,findIndex会吐出满足判定条件的数据序号
isEmpty用于检查一个上游Observable对象是不是“空的”,所谓“空的”Observable是指没有吐出任何数据就完结的Observable对象
defaultIfEmpty接收一个参数(默认值),如果上游Observable是空的,吐出默认值,如果不是空的,吐出Observervable的数据。

过滤类操作符

过滤类操作符

操作符说明
filter过滤
first(无参)上游的Observerable对象的第一个数据;(一参):符合条件的第一个;(二参)可选:结果选择器===》 例如:{(value,index)=>[value,index]把代表满足条件的值和序号合并为一个数组,传递给下游}
last与first相反,取符合条件的最后一个数据
take取出
takeLasttakeLast只有确定上游数据完结的时候才能产生数据,而且是一次性产生所有数据.
takeWhile接受一个判定函数作为参数,这个判定函数有两个参数,分别代表上游的数据和对应的序号,takeWhile会吐出上游数据,直到判定函数返回false,只要遇到第一个判定函数返回false的情况,takeWhile产生的Observable就完结
takeUntil其参数是另一个Observable对象notifier,由这个notifier来控制什么时候结束从上游Observable拿数据,因为notifier本身又是一个Observable,吐出数据可以非常灵活。
skip跳过前N个之后全拿
skipWhile只要某个数据让判定函数返回false,之后skipWhile就不做跳过的动作,所有的上游数据都转手给下游。
skipUntil

takeUntil
作为takeUntil的notifier参数如果在吐出数据或者完结之前抛出了错误,那takeUntil也会把这个错误抛给下游,从而关闭了上下游之间的通道 `const source$ = Observable.interval( 1000); const notifier$ = Observable.throw(’ custom error’); const takeUntil$ = source . t a k e U n t i l ( n o t i f i e r .takeUntil( notifier .takeUntil(notifier);4-1

回压控制

回压控制示意图
4-2

有损回压控制

有损回压控制会丢掉一些数据

操作符说明
·throttleTime参数为 duration; 限制在duration时间范围内,从上游传递给下游数据的个数。在“节流时间”范围内,throttle把第一个数据传给下游;throttleTime获得上游数据之后先扔给下游,然后开始计时
·debounceTime参数为dueTime; 让传递给下游的数据间隔不能小于给定的时间dueTime;debounceTime要等上游在dueTime毫秒范围内不产生任何其他数据时才把这个数据传递给下游,如果在dueTime范围内上游产生了新的数据,那么debounceTime就又要重新开始计时。
·auditTime在“节流时间”范围内,audit是把最后一个数据传给下游。
·sampleTimesampleTime会记录每一个时间块上游推下来的最后一个数据,到了每个时间块结尾,就把这个时间块上游的最后一个数据推给下游
distinct去重
distinctUntilChanged和上一个数据相比,淘汰掉与上一个数据相同的数据;
·ignoreElement忽略所有上游数据,只关心complete和error事件;
·elementAt把上游数据当数组,只获取指定下标的那一个数据;
·single来检查上游是否只有一个满足对应条件的数据,如果答案为“是”,就向下游传递这个数据;如果答案为“否”,就向下游传递一个异常。

throttleTime
const source$ = Observable.interval( 1000); const result$ = source$.throttleTime( 2000);
在这里插入图片描述

debounceTime
debounceTime

auditTime
在这里插入图片描述

sampleTime
在这里插入图片描述

转化类操作符

映射操作符

操作符说明
map将数据执行相应的函数吐给下游
mapTo将上游所有的值 返回给下游相同的值
pluck将上游数据中的特定值“拔” 给下游 (例如:const source$ = Observable.of( {name: 'RxJS', version: 'v4'}, {name: 'React', version: 'v15'}); const result$ = source$.pluck('name');

无损回压控制

windowTime & bufferTime

根据时间来缓存上游的数据,用一个参数来指定产生缓冲窗口的间隔

windowTime: 上游的每个数据都被传送给对应时间区间的内部Observable对象中,当时间区域的时间一到,这个区间的内部Observable对象就会完结。
在这里插入图片描述

bufferTime产生的是普通的Observable对象,其中的数据是数组形式,bufferTime会把时间区块内的数据缓存,在时间区块结束的时候把所有缓存的数据放在一个数组里传给下游在这里插入图片描述

区别: windowTime把上游数据传递出去是不需要延迟的,而bufferTime则需要缓存上游的数据,这也就是其名字中带buffer(缓存)的原因。

windowCount & bufferCount

根据数据个数来界定区间

windowWhen & bufferWhen

它们接受一个函数作为参数,这个参数名为closingSelector,closingSelector应该返回一个Observable对象,用于控制上游的数据分割,每当返回的Observable对象产生数据或者完结时,windowWhen就认为是一个缓冲区块的结束,重新开启一个缓冲窗口。

windowToggle & bufferToggle

windowToggle和bufferToggle需要两个参数,第一个参数opening 是 一 个 O b s e r v a b l e 对 象 , 每 当 o p e n i n g 是一个Observable对象,每当opening Observableopening产生一个数据,代表一个缓冲窗口的开始,同时,第二个参数closingSelector也会被调用,用来获得缓冲窗口结束的通知。

const source$=Observable.timer(0,100);
const openings$=Observable.timer(0,400);
const closingSelector= value => {
	return value%2 === 0? Observable.timer(200):Observable.timer(100);
};
const result$=source$.windowToggle(openings$,closingSelector);

在这里插入图片描述

window & buffer

window只支持一个Observable类型的参数,称为notifier , 每 当 n o t i f e r ,每当notifer notifer产生一个数据,既是前一个缓存窗口的结束,也是后一个缓存窗口的开始。

高阶Map

操作符说明
concatMap顺序连接不同Observable对象中数据; (例如 按钮拖拽的数据流)
mergeMap任何内部Observable对象中的数据,来一个给下游传一个,不做任何等待;(例如: ajax)
switchMap后产生的内部Observable对象优先级总是更高,只要有新的内部Observable对象产生,就立刻退订之前的内部Observable对象,改为从最新的内部Observable对象拿数据。
exhaustMap与switchMap 相反,先产生的内部Observable优先级总是更高,后产生的内部Observable对象被利用的唯一机会,就是之前的内部Observable对象已经完结。
concatMapTo参数直接就是一个Observable对象,而不是一个返回Observable对象的函数
mergeMapTo
switchMapTo

数据分组

操作符说明
groupBy分组;
partition一分为二;
scanscan和reduce的区别在于scan对上游每一个数据都会产生一个规约结果,而reduce是对上游所有数据进行规约,reduce最多只给下游传递一个数据;
mergeScanmergeScan类似于scan,不过规约函数返回的是Observable对象而不是一个数据;

异常错误处理

在这里插入图片描述

RxJS 对异常错误的处理主要分为:恢复和重试

操作符说明
catch主要用于恢复,捕获到上游的错误数据进行 处理;
retryretry这个操作符的作用就是让上游的Observable重新走一遍,达到重试的目的;
retryWhenretryWhen接受一个函数作为参数,这个参数称为notifer,用于控制“重试”的节奏和次数;
finally用于执行无论出错还是不出错都要做的事情。

多播

Subject(主体),一个Subject既有Observable的接口,也具有Observer的接口,一个Subject就具备上述的两个职责。
Subject有状态,这个状态就是所有Observer的列表,所以,当调用Subject的next函数时,才可以把消息通知给所有的Observer。
Subject对象是不能重复使用的,所谓不能重复使用,指的是一个Subject对象一旦被调用了complete或者error函数,那么,它作为Observable的生命周期也就结束了,后续还想调用这个Subject对象的next函数传递数据给下游,就如同泥牛入大海,一去不回,没有任何反应。

const subject = new Subject<number>();
subject.subscribe({
  next: (v) => console.log(`observerA: ${v}`)
});
subject.subscribe({
  next: (v) => console.log(`observerB: ${v}`)
});
subject.next(1);
subject.next(2);

多播操作符

操作符说明
multicast以上游的Observable为数据源产生一个新的HotObservable对象。
publish封装了multicast和创建一个新Subject对象这两个动作。
share是一个ConnectableObservable对象,而且是一个调用了refCount的ConnectableObservable对象,可以自动根据Observer的多少来对上游进行subscribe和unsubscribe。
publishLast(对应AsyncSubject)使用了AsyncSubject的publishLast产生的ConnectableObservable对象,最多只会包含一个数据,也就是上游吐出来的最后一个数据。同时,AsyncSubject对象表现得“可以重用”,即使上游ColdObservable完结时候调用了AsyncSubject对象的complete方法,之后添加的Observer依然可以从AsyncSubject中获得数据,而且是立刻获得数据,当然,这立刻获得数据就是上游吐出的最后一个数据。
pubishReplay(对应ReplaySubject) ReplaySubject就像是录像,它不只是支持多播,而且还能够支持“录播”,能够把上游传下的数据存储下来,当新的Observer添加时,让Observer能够接收到订阅之前产生的数据。
publishBehavior(对应BehaviorSubject),的行为,BehaviorSubject就是可以提供一个“默认数据”,当添加Observer的时候,即使上游还没有吐出数据Observer也会立即获得这个“默认数据”;而且,这个“默认数据”总是会被上游吐出的最新数据替代,也就是说,任何新添加的Observer都有一个大礼包在等着他,这个大礼包要么是指定的“默认数据”,要么就是上游吐出的最新数据

Scheduler 时间调度

简介

JavaScript是单线程运行

调用栈在这里插入图片描述

事件循环的工作方式
在这里插入图片描述

任务队列
当调用栈处理完一个任务,准备迎接下一个任务的时候,“事件循环”总是会优先看一看MicroTask的队列,只要还有MicroTask存在,就直接把MicroTask交给调用栈,其他MacroTask队列的任务都只能等下次机会。
在这里插入图片描述

调度器分为3类:
1.queue 同步执行;
2.asap 利用的是Micro Task;
3.async 利用的是是 Macro Task;

创建和合并操作符

操作符说明
bindCallback创建类
bindNodeCallback创建类
empty创建类
from创建类
fromPromise创建类
interval创建类
of创建类
range创建类
throw创建类
timer创建类
concat合并类
merge合并

observeOn

observeOn :按 照 函 数 式 编 程 的 原 则, 当 然 不 应 该 在 函 数 中 去 修 改 上 游 的 Observable 对 象, 但 是 可 以 根 据 这 个 Observable 对 象 产 生 出 一 个 新 的 Observable 对 象 出 来, 让 这 个 新 的 Observable 对 象 吐 出 的 数 据 由 指 定 的 Scheduler 来 控 制。

observeOn 在 数 据 流 管 道 接 近 末 尾 的 位 置 使 用, 最 好 就 是 在 调 用 subscribe 之 前, 因 为 通 常 RxJS 默 认 的 Scheduler 已 经 足 够 合 理, 如 果 我 们 要 修 改 Scheduler, 在 整 个 数 据 管 道 中 只 需 要 修 改 一 处 就 足 够, 既 然 数 据 管 道 中 的 数 据 是 被 Observer 使 用, 当 然应 该 在 添 加 Observer 的 subscribe 调 用 之 前 使 用 observeOn 调 整 Scheduler 最 合 适

const source$ = Observable.range( 1, 3); 
const asapSource$ = source$.observeOn( asap); 
console.log(' before subscribe'); 
asapSource$.subscribe( 
	value => console.log('data: ', value), 
	error => console.log('error: ', error), 
	() = > console.log('complete') 
); 
console.log(' after subscribe');

subscribeOn

控 制 添 加 Observer 的 时 机。 参数:queue,async,asap

学习《程墨. 深入浅出RxJS (实战)》笔记

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值