RxJS入门
- 简介
- RxJS英文全称是Reactive Extensions for JavaScript,响应式扩展的js;是一个使用可观察序列编写异步和事件程序的库。
- ReactiveX将观察者模式、迭代器模式、函数式编程和集合相结合,以便满足对管理事件序列的一种理想方式。
- RxJS解决异步事件管理的基本概念如下:
- Observable:可观察者,表示未来有值或事件的可调用集合的概念,其实就是定义一个懒加载的函数对象,可以稍后被调用也可以不被调用,在调用的时候将会生产数据。
- Observer:观察者,它是一个回调的集合,所以它监听了Observable传递的值并且可以做处理,其实就是用于消费Observable生产的数据。
- Subscription:订阅或者消费,表示Observable的执行,当订阅了一个Observable后将返回Subscription对象,此对象主要用于取消订阅。
- Operators:操作函数,可以使用函数式编程风格来处理具有map、filter、concat、reduce等操作的集合。(回头再补充)
- Subject:主题,相当于事件发送者(EventEmitter),也是将一个值或者事件传播到多个Observable的唯一方式(其实就是Observable的多播实现方式)
- Schedulers:调度者,是控制并发的集中调度程序,当发生像setTimeout、requestAnimationFrame等其他计算的时候允许我们进行协调。
- Observable
- 可观察者是多个值的惰性Push集合,常见的Pull对象有Function、Iterator,Promise是单值的Push
- 惰性代表是懒加载,定义好的Observable并不会被立即执行,如下:
查看代码
import { Observable } from 'rxjs'; const observable = new Observable(subscriber => { subscriber.next(1); subscriber.next(2); setTimeout(() => { subscriber.next(3); subscriber.complete(); }, 1000); });
- 只有当订阅了此Observable才会被调用,如下:
查看代码
import { Observable } from 'rxjs'; const observable = new Observable(subscriber => { subscriber.next(1); subscriber.next(2); setTimeout(() => { subscriber.next(3); subscriber.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 just after subscribe got value 3 done
- Pull和Push是两种不同的协议,用于描述生产者与消费者如何通讯的方式,说白了,Pull就是消费者主动去要数据,Push就是消费者被动接收数据(类似于等待生产者告诉你有数据了你才会去消费)。
- 在Pull系统中,消费者将确定什么时候获取生产者的数据,生产者本身不知道何时将会被数据给消费者,像Function这种,只有当被调用的时候才会产生数据,又或者像Iterator,只有调用其next方法的时候才会产生数据
- 在Push系统中,生产者决定何时向消费者发送数据,消费者不知道何时会收到该数据,一般都是使用回调的方式来实现被动消费
- Observable是同步执行的,只不过它也支持异步(比如说Http请求或者定时器)
- 同步调用普通方法如下:
查看代码
function foo() { console.log('Hello'); return 42; } console.log('before'); console.log(foo.call()); console.log('after');
- 输出结果如下:
查看代码
"before" "Hello" 42 "after"
- 使用Observable执行如下:
查看代码
const foo = new Observable(subscriber => { console.log('Hello'); subscriber.next(42); }); console.log('before'); foo.subscribe(x => { console.log(x); }); console.log('after');
- 输出结果如下:
查看代码
"before" "Hello" 42 "after"
- Observables 可以随着时间的推移“返回”多个值,但是Function不可能进行多次return
查看代码
import { Observable } from 'rxjs'; const foo = new Observable(subscriber => { console.log('Hello'); subscriber.next(42); subscriber.next(100); // "return" another value subscriber.next(200); // "return" yet another }); console.log('before'); foo.subscribe(x => { console.log(x); }); console.log('after');
- 输出结果如下:
查看代码
"before" "Hello" 42 100 200 "after"
- 创建
- 使用Subscribe函数作为参数的方式创建Observable,如下(其他方式在Observer介绍中说明)创建了一个Observable对象,在被订阅后将每秒发送一个hi的字符给消费者:
查看代码
import { Observable } from 'rxjs'; const observable = new Observable(function subscribe(subscriber) { const id = setInterval(() => { subscriber.next('hi') }, 1000); });
- Observable可以使用new的方式创建,最常见的方式应该是使用of、from、interval、httpClient请求等方式
- 订阅
- 可以使用如下方式对上面创建的Observable进行订阅:
查看代码
observable.subscribe(x => console.log(x));
- 订阅一个Observable对象就像调用一个函数,产生的数据将会被回传到回调函数中(Subscribe函数)
- 执行
- 创建描述中的Subscribe其实代表的就是Observable的execution
- Execution可以传递三种类型的值:
- next:发送数值、字符串、对象等。
- error:发送JavaScript的错误或者异常。
- complete:不发送值,仅作为正常完成后的自定义处理。
- next是最重要的,支持无限个next的执行,必须要实现;而error和complete如果触发的话那么只会触发其中一个方法,因为如果异常了,那么就不会到complete中,如果到了complete中那么肯定没发生异常
- Observable严格按照Observable契约,所以在complete以后是不会再执行next的,如下:
查看代码
import { Observable } from 'rxjs'; const observable = new Observable(function subscribe(subscriber) { subscriber.next(1); subscriber.next(2); subscriber.next(3); subscriber.complete(); subscriber.next(4); // 此处将不会被执行,因为在这前面以后调用了complete方法 });
- 我们可以在Subscribe函数中进行try-catch,有效的捕捉异常,如下:
查看代码
import { Observable } from 'rxjs'; const observable = new Observable(function subscribe(subscriber) { try { subscriber.next(1); subscriber.next(2); subscriber.next(3); subscriber.complete(); } catch (err) { subscriber.error(err); // 观察者中任意位置发生了异常都会执行此回调函数 } });
- 取消
- 当你订阅时,你会得到一个订阅,它代表正在进行的执行。只需调用
unsubscribe()
即可取消执行 -
当我们使用创建 Observable 时,每个 Observable 都必须定义如何处理该执行的资源
create();
您可以通过unsubscribe
从内部返回自定义函数来做到这一点function subscribe()
例如,这是我们清除间隔执行集的方式
setInterval
: 查看代码
const observable = new Observable(function subscribe(subscriber) { // Keep track of the interval resource const intervalId = setInterval(() => { subscriber.next('hi'); }, 1000); // 提供一种取消和处理interval资源的方法 return function unsubscribe() { clearInterval(intervalId); }; }); // 执行订阅 const unsubscribe = observable.subscribe(x => console.log(x)); // 在两秒以后取消订阅,将会调用上面重写的unsubscribe方法来停止hi的输出并且清楚interval资源 setTimeout(() => { unsubscribe.unsubscribe(); }, 2000);
- 当你订阅时,你会得到一个订阅,它代表正在进行的执行。只需调用
- 与其他类型的比较
- Function是一种延迟计算的计算,它在调用时同步返回单个值。
- Iterator是一种延迟计算的计算,它在迭代时同步返回零到(可能)无限值。
- Promise是一种可能(或可能不会)最终返回单个值的计算。
- Observable是一种延迟计算的计算,从它被调用的那一刻起,它可以同步或异步返回零到(可能)无限值。
- 可观察者是多个值的惰性Push集合,常见的Pull对象有Function、Iterator,Promise是单值的Push
- Observer
- Observer 是 Observable 的消费者(Observable会生产数据,Observer对这些数据进行消费)。观察者只是一组回调,对应于 Observable 传递的每种类型的通知:
next
、error
和complete
。下面是一个典型的 Observer 对象的例子:查看代码
// 定义一个用于消费的observer const observer = { next: x => console.log('Observer got a next value: ' + x), error: err => console.error('Observer got an error: ' + err), complete: () => console.log('Observer got a complete notification'), }; // 对observable进行订阅的时候将observer传入进行,用于消费数据 observable.subscribe(observer); // 也可以使用简单的方式,不用传入observer对象,只需要传入一个回调函数就行 observable.subscribe(x => console.log('Observer got a next value: ' + x));
- Observer 是 Observable 的消费者(Observable会生产数据,Observer对这些数据进行消费)。观察者只是一组回调,对应于 Observable 传递的每种类型的通知:
- Operators
- Operator是一种基于Observable实例进行运算的函数,比如说过滤、合并、求平方...
- Operator分两种,一种是管道操作符,一种是创建操作符
- 管道操作符主要是将第一个Observable作为参数输入并且返回一个新的Observable,但是其订阅逻辑是基于第一个Observable
- 创建操作符主要是以独立函数的方式创建新的Observable
- 使用创建操作符创建一个新的Observable并且使用管道操作符进行求平方的运算,如下代码:
查看代码
import { of } from 'rxjs'; import { map } from 'rxjs/operators'; of(1, 2, 3) .pipe(map((x) => x * x)) .subscribe((v) => console.log(`value: ${v}`)); // Logs: // value: 1 // value: 4 // value: 9
- 上述代码中使用of创建一个新的Observable,并且使用map来对每一个值进行求平方,注意,在of后面使用了pipe方法,主要是因为operator就是普通的函数,可以被连续调用,所以如果有多个operator被调用,其代码就会变得可读性很差,比如说of(1,2,3).op4()(op3()(op2()(op1()(obs)))),所以使用pipe方式就是为了使其更具有可读性。
- 使用pipe()函数生成新的操作符
- 有时候我们想把多个运算符写到一个方法里面,方便去重用或者方便定义,那可以使用pipe来实现(这个pipe和observable上的pipe名字一模一样,官方也没解释为什么这样命名),如下:
查看代码
import { pipe } from 'rxjs'; import { filter, map } from 'rxjs/operators'; discardOddDoubleEven() { return pipe( filter<number>((v) => !(v % 2)), map((v) => v + v) ); } of(1, 2, 3) .pipe(this.discardOddDoubleEven()) .subscribe((v) => console.log(`value: ${v}`)); // 创建一个只对偶数值就行翻倍的函数 // Logs: 4
- Rxjs提供的运算符很多,这里就不赘述了,请查看官网,按需使用:Rxjs操作符
- Subscription
- Subscription 是一个表示一次性资源的对象,对Observable的执行会返回一个Subscription对象,此对象有一个重要的无参方法unsubscribe,它是指取消订阅,我们可以在此方法中释放自己需要释放的资源,如下:
-
查看代码
const observable1 = interval(1000); const subscription = observable1.subscribe(x => console.log(x)); // Later: 两秒以后将取消订阅,控制台中在输出了0和1以后将不再输出日志 setTimeout(() => { subscription.unsubscribe(); }, 2000); // Logs:0 // 1
-
- Subscription可以添加ChildSubscription,并且在Subscription调用unsubscribe方法以后将ChildSubscription也一并取消了,如下:
查看代码
import { interval } from 'rxjs'; const observable2 = interval(400); const observable3 = interval(300); const subscription1 = observable2.subscribe(x => console.log('first: ' + x)); const childSubscription = observable3.subscribe(x => console.log('second: ' + x)); subscription1.add(childSubscription); setTimeout(() => { // 此处将取消subscription1和childSubscription的订阅 subscription1.unsubscribe(); }, 1000); // Logs:second: 0 // first: 0 // second: 1 // first: 1 // second: 2
- Subscription 是一个表示一次性资源的对象,对Observable的执行会返回一个Subscription对象,此对象有一个重要的无参方法unsubscribe,它是指取消订阅,我们可以在此方法中释放自己需要释放的资源,如下:
- Subject
- 主题主要是用于多播场景,等用到的时候或者时间充足的时候再来记录吧,请查看官网:主题
- Schedulers
- 调度器这个可能涉及到原理,等深入去玩Angular的时候再来记录吧,请查看官网:调度器
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】博客园社区专享云产品让利特惠,阿里云新客6.5折上折
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· CSnakes vs Python.NET:高效嵌入与灵活互通的跨语言方案对比
· DeepSeek “源神”启动!「GitHub 热点速览」
· 我与微信审核的“相爱相杀”看个人小程序副业
· Plotly.NET 一个为 .NET 打造的强大开源交互式图表库
· 上周热点回顾(2.17-2.23)