RxJS 系列 – Observable & Creation Operators

前言

RxJS 最大篇幅就是一堆的 operators, 但是那些概念并不多, 只要常用就能熟能生巧了.

Observable 和 Subject 反而需要我们了解清楚.

所以这篇我们先来了解这 2 大概念的其中一个 Observable.

 

参考

Docs – Observable

 

Observable / RxJS 的使用场景

因为 RxJS 基本上就是 Observable, 所以为什么要用 Observable 相等于为什么要用 RxJS.

在使用 JS, DOM, BOM 的时候, 我们经常面对不同的接口, 比如 addEventListener, Intersection Observer, XMLHttpRequest 等等

它们使用的 API 都不一样, 但都有一个共同的特性, 那就是都有 callback, 都会触发多次.

这时 RxJS 就可以作为一个统一的管理手段. 这个和 C# 的 LINQ 是一样的. LINQ 的诞生就是为了统一所有对集合的操作. 不管你是 DataTable, Array, List 最终都可以使用 LINQ 统一接口来操作.

 

如何理解 Observable

我们在学习新东西时, 往往会从它长得像以前认识的某些东西作为入手. 但如果一个新东西跟以前许多东西像, 但又不像时, 这个方法反而会制造出混乱.

从概念上去理解 Observable 就有点这种混乱的感觉. 因为它带有 Generator Function 的某些特性, 但又缺失一些特性, 然后它又有点观察者模式的味道, 但又不完全是.

所以呢, 我们干脆先把它当作新物种. 在慢慢把旧事物套进去, 这样来学习它.

 

Observable 和 观察者模式

Observable 创建与订阅

Observable 是一个 class, 创建的方式就是 new

const observable = new Observable();
observable.subscribe(() => {
  console.log('calling');
});

顾名思义, 它肯定和观察者模式有关系. 在看看它的方法 .subscribe()

subscribe 就是订阅, 和 addEventListener 差不多意思.

但上面的代码中, 只表达了订阅的逻辑, 也就是当事件触发后想做些什么. 但却没有表明事件什么时候会触发.

所以下面这个才是完整的 Observable

const observable = new Observable<string>(subscriber => {
  setInterval(() => {
    subscriber.next('passing event');
  }, 2000);
});
observable.subscribe(event => {
  console.log('calling', event);
});

new Observable 时传入一个方法. 这个方法会在 subscribe 时被调用. 通过参数得到 subscriber 对象.

在这个方法内, 就可以表达 event 发布的逻辑. 比如 setInterval 让它每一秒发布一次 subscriber.next 就是 dispatch event 的意思.

至此, 一个完整的订阅和发布就诞生了. 它看上去就是个观察者模式.

Multiple Subscribe

从订阅发布逻辑来看, 它确实就是观察者模式的体现. 但是它有一点很特别.

当我们 2 次调用 subscribe 时, 会发现它的表现就和典型的观察者模式不一样了.

observable.subscribe(event => {
  console.log('calling1', event);
});
observable.subscribe(event => {
  console.log('calling2', event);
});

典型的观察者, 会把所以订阅方法放到一块, 当时间触发时挨个挨个去调用.

但 subscribe 却不同. 每一次调用 subscribe, Observable 的初始方法都会被调用. 然后生成一个独立的时间线.

像上面的例子, 2 次 subscribe 诞生了 2 个独立的 interval. 它们发布的时机是不同的.

以上就是 Observable 又像又不像观察者模式的几个点.

 

Observable 和 Generator Function

上面说到, 每一次调用 subscribe 都会产生独立的 stream (每一次初始化方法都会执行一遍).

这个跟 Generator Function 有点像. 哪里像? 2 个点

1. 调用函数, 返回多次值. Generator Function 和普通函数最大的区别是它可以返回多次值.

2. 每一次调用得到的返回都是相互独立的. 

那哪里不一样呢? 一个是 pull 一个是 push.

参考: RxJS——Observable 和生成器,推模式和拉模式

Generator Function 属于 pull, 调用函数后会得到 iterator, 当想获取值的时候 for...of iterator 就可以了. 所以主导权在调用方.

Observable 属于 push, 调用函数后不会得到 iterator, 相反它是提供了一个 callback 方法给发布方. 然后静静等待发布方发布值.所以主导权在发布方.

以上就是 Observable 又像又不像 Generator Function 的几个点.

 

小结

综上, Observable 带有观察的订阅发布的概念, 但它每一次订阅都执行初始化函数, 形成独立的 stream, 

后半段这点和 Generator Function 很像, 唯一的区别是 pull 和 push 主导权的不同.

 

同步 和 异步

由于大部分 RxJS 都是用来处理异步事件. 这让我们以为 RxJS 和 Promise 一样一定是异步的. 但其实不然.

import { Observable } from 'rxjs';

const observable = new Observable(subscriber => {
  console.log('1');
  subscriber.next();
  console.log('3');
});

console.log('0');
observable.subscribe(() => {
  console.log('2');
});

console.log('4');

上面是一个同步执行的例子. 如果初始化函数里面用到了异步方法, 比如 setTimeout, 那么它才会变成异步的

 

next, error, complete

上面提到了 next 相等于 dispatch (发布). 其实还有 2 个特别的 dispatch, 一个是 complete, 一个是 error.

complete 表示这个 stream 结束了, 再也不会有值发布了.

error 则表示 stream 内部报错了, error 以后 stream 也结束了, 不能再发送任何值.

const observable = new Observable<string>(subscriber => {
  subscriber.next('passing event');
  subscriber.error('error');
  subscriber.complete();
  return () => console.log('dispose');
});

observable.subscribe({
  next: () => {
    console.log('next');
  },
  complete: () => {
    console.log('complete');
  },
  error: () => {
    console.log('error');
  },
});

当 dispatch error 以后, complete 不会触发, dispose 会触发. 上面的 console 是 next > error > end

如果把 error 注释掉, 那么它的 console 是 next > complete > end

 

如何退订 subscribe 与 如何 dispose Observable

unsubscribe

const subscription = observable.subscribe(event => {
  console.log('calling1', event);
});
subscription.unsubscribe();

调用 subscribe 以后会返回一个 subscription 对象. 这个对象就带有 unsubscribe 方法.

如果有 multiple subscription 也可以集合起来一起 unsubscribe.

const subscriptionCollection = new Subscription(); // 集合
const subscription = observable.subscribe(event => {
  console.log('calling1', event);
});
subscriptionCollection.add(subscription); // 添加
subscriptionCollection.unsubscribe(); // 一起退订

Subscription 还有一个 closed 属性, 用来判断 stream 是否已经 completed, error 或者 unsubscribe。

提醒:如果把一个 subscription add 进一个已经 unsubscribe 的 Subscription 对象里,它会马上被 unsubscribe。

Observable dispose

上面提到, 每一次 subscribe 都会开启一个新的 Interval. 这些资源应该在退订后得到释放. 这就是 dispose 的职责.

const observable = new Observable<string>(subscriber => {
  const interval = setInterval(() => {
    subscriber.next('passing event');
  }, 2000);
  // 返回 dispose 方法
  return () => {
    clearInterval(interval); // 释放资源
  }
});

初始化方法可以返回一个 dispose 方法. 当退订后这个方法会被执行. 于是里面就可以做一些释放资源的事情了。

Listening to unsubscribe event by finalize operator

unsubscribe 不会导致 Observable 发布 next, error, complete,只会执行初始化方法返回的 dispose 方法。

如果我们无法添加逻辑代码到 dispose 方法里,但有希望在 unsubscribe 时做点事情,那我们可以使用 finalize operator。

如果我们想知道一个 Observable 被 unsubscribe 了的话,需要通过 finalize 功能

 

Create Observable by Operators

参考

RxJS 建立類型 Operators (1) - EMPTY / of / range / iif / throwError / ajax

RxJS 建立類型 Operators (2) - from / fromEvent / fromEventPattern / interval / timer / defer

Docs – Creation Operators

new Observable 是底层创建 Observable 的手法. RxJS 封装了许多 Operators 方便我们创建 Observable.

所以在开发项目时, 直接 new Observable 是比较少见的.

以下都是 RxJS 封装好的方法.

下面是我常用的

fromEvent

它用来替代 addEventListener

const buttonClicked$ = fromEvent(document.querySelector('button')!, 'click');
buttonClicked$.subscribe(() => {
  console.log('clicked');
});

fromEventPattern

它是底层的 fromEvent, fromEvent 的 target 必须实现 interface addEventListener, 但偶尔会遇上一些 target 没有实现这个 interface.

这时就只能用 fromEventPattern 来自定义如何实现 addEventListener 和 removeEventListener 了.

const button = document.querySelector('button')!;
const clicked$ = fromEventPattern(
  handler => {
    button.addEventListener('click', handler); // when click dispatch, handle will be called
    return 'returnValue'; // can return whatever here
  },
  (handler, returnValue) => {
    button.removeEventListener('click', handler);
    console.log(returnValue); // use returnValue for whatever
  },
);
const sub = clicked$.subscribe(event => console.log('clicked', event));
sub.unsubscribe();

interval

用来替代 setInterval

const interval$ = interval(1000);
interval$.subscribe(count => {
  console.log(count); // 0..1..2..3
});

当 unsubscribe 后, 内部的 interval 也会自动被释放, RxJS 替我们封装好了.

timer

用来替代 setTimeout 和实现 delayable interval

timer 是 interval 的底层实现,使用它,我们可以控制的更多。

timer 有 2 个参数

第一个是 delay

第二个是 duration

对比 interval 只有 duration, 多了 delay 功能. interval 的行为是这样的, 如果 duration 是 1 秒, 那么第一次触发就是在 1 秒后, 然后第二次又再一秒后.

timer 的 delay 可以控制第一次的 1 秒. 

timer(0, 1000) 表示立刻触发第一次, 然后接着每一秒再触发

timer(3000, 1000) 表示, delay 3 秒后触发第一次, 然后接着每一秒再触发

timer(1000) 如果没有提供 duration 参数, 那么它就是 delay 1 秒后触发, 然后就结束了. 这就相等于实现了 setTimeout

of

of 的目的是把 variable 转化成 Obserable

of(10).subscribe(value => console.log(value)); // 立马触发 console 10

它有啥用呢? 

因为 RxJS 有一种万物皆是 Observable 的概念, 统一管理嘛.

许多 RxJS 的 operators 参数或返回值都必须是 Observable 类型, 所以 of 就可以到达 cast 类型的目的.

from

from 和 of 目的都是 cast, 但是 of 用于普通变量

from 用 Promise, Iterable

of([1, 2, 3]); // 触发 1 次, value = [1,2,3] Array
from([1, 2, 3]); // 触发 3 次, value = 1 Number

从上面这个例子可以看出它们的不同之处

不只是 array, from 可以处理任何 Iterable.

另外, from 还经常用于将 Promise 转成 Obserable

of(Promise.resolve(1)); // 得到的是 Promise 对象
from(Promise.resolve(1)); // 得到的是 Number 1

iif

iif 是 conditional create Observable 方法. 类似一个 Ternary operation 三元运算符

iif(() => true, of(1), of(2)).subscribe(v => console.log(v)); // 1

1 个参数是判断函数

2 是 true 情况下返回的 Observable

3 是 false 情况下返回的 Observable

每一次 subscribe 调用的时候, 判断函数都会被执行获取最新的判断值 (所以它是一个函数而不是一个固定的变量).

defer

defer 有延后执行的意思. 它通常拿来延后 of

of('variable') 会先把 variable 存起来, 当 subscribe 调用时立刻返回这个 variable

defer(() => of('can be a random variable')) 则不会把 variable 存起来, 每一次调用 subscrube 的时候, 会执行创建 observable 函数.

得到一个新的 Observable 然后订阅它. 

Empty 和 throwError

Empty 是一个直接 complete 的 Observable,一旦 subscribe 它立马就发布 complete。

throwError 是一个直接 error 的 Observable,一旦 subscribe 它立马就发布 error。

EMPTY.subscribe({
  next: () => console.log('next'), // won't call
  complete: () => console.log('complete'), // direct complete 了
});

throwError(() => 'error message').subscribe({
  error: message => console.log(message), // direct error 了
  complete: () => console.log('complete'), // won't call
});
它们不用于创建一个 Observable 起点,通常用于 pipe 中 operator 的返回。

没有介绍到的 Creation Operators

ajax

bindCallback

bindNodeCallback

generate

range

一句话总结

fromEvent : 替代 addEventListener

fromEventPattern : 自定义 addEventListener 的方式

interval : setInterval

timer : interval 的扩展, 可以实现 setTimeout

of : Variable to Observable

from : convert Iterable and Promise

iif : conditional 三元 create Observable

defer : 延迟 of('variable') 

empty : complete

throwError : error

 

posted @ 2022-09-30 23:49  兴杰  阅读(543)  评论(0编辑  收藏  举报