重学 Rxjs —— 创建操作符

368 阅读5分钟

「这是我参与2022首次更文挑战的第22天,活动详情查看:2022首次更文挑战

这一节我们来看创建操作符,从名字可以看出来这些操作符可以创建 Observable 对象,Observable 对象是 Rxjs 中一切的源头,因此这些操作符很常用。在 Rxjs 中,一切事物都可以看作 Observable:点击事件、网络请求、同步数据等,而所有 Observable 有都可以使用操作符进行统一化处理,这样就建立了一个抽象层,使得无论是什么来源,无论是同步还是异步,一切的处理都可以进行统一。

of

of 是最简单的创建操作符,它可以接收任意个参数,按照顺序将所有的参数同步发出然后结束,举个例子:

of(1, 2, 3).subscribe(
  next => console.log(next),
);
// 1
// 2
// 3
of([1, 2, 3]).subscribe(
  next => console.log(next),
);
// [1, 2, 3]

它的弹珠图也很简单:

image.png

from

from 是非常强大的一个操作符,数组、类数组对象、Promise、迭代器、类 Observable 对象都可以使用 from 转化为 Observable。举个例子:

from([1, 2, 3]).subscribe(
  next => console.log(next),
);
// 1
// 2
// 3

这里注意 from 和 of 的区别,of 只是把数据发送出来,而 from 是转化,对于数组,它的转化规则是从头至尾遍历数组,把每一项内容按照顺序发出来然后结束,因此这里得到的是每一项数组元素而不是整个数组。

对于 Promise 类型,from 转换规则是把 then 传出来的内容发出来;对于迭代器类型,会输出每次 next 的内容;类数组对象和类 Observable 对象转化规则也比较容易理解。

弹珠图:

image.png

fromEvent

从命名也可以看出来 fromEvent 是把 event 转化为 Observable,这个 event 可以是一个 DOM 事件或者一个 EventEmitter 事件,事件每触发一次就会在 Observable 中发出一个 event:

const clicks = fromEvent(document, 'click');
clicks.subscribe(x => console.log(x));

上面的例子每点击一次都会打印一次,它的弹珠图也很简单:

image.png

fromEventPattern

fromEventPattern 实现的效果和 fromEvent 类似,不过它接收的参数不同,它是把事件处理函数转化为 Observable,上面 fromEvent 的例子可以改为使用 fromEventPattern 来实现:

function addClickHandler(handler) {
  document.addEventListener('click', handler);
}
function removeClickHandler(handler) {
  document.removeEventListener('click', handler);
}
const clicks = fromEventPattern(
  addClickHandler,
  removeClickHandler
);
clicks.subscribe(x => console.log(x));

除了写法上不同,它们实现效果上是一致的,这样写起来更加灵活,理论上使用 fromEvent 实现的功能都可以改为使用 fromEventPattern 来实现,大部分时候使用 fromEvent 就可以,fromEvent 无法满足需求时需要改用 fromEventPattern。

弹珠图和 fromEvent 相同:

image.png

interval

每隔一段时间发出一个递增数字,参数为时间间隔:

interval(1000)subscribe(x => console.log(x));
// 0
// 1
// 2
// ...

在实际应用中,这类操作符往往用来与其他操作符共同使用处理流程控制等场景,因此其发送的内容本身并不重要,通常默认都是递增序列。

弹珠图:

image.png

timer

等待一段时间后发出一个数字,如果设置第二个参数会定时发出递增数字,直接看例子:

timer(1000).subscribe(val => console.log(val));

上面的例子会在一秒之后发出一个 0,我们添加第二个参数:

timer(1000, 2000).subscribe(val => console.log(val));

此时的效果是先等待 1 秒,发出 0,然后过两秒发出 1,再过两秒发出 2,一直持续下去,这个效果和 interval 很相似,实际效果 interval(x) 等价于 timer(0, x)。

repeat

重复已经结束的流,参数为重复次数:

of(1, 2).pipe(repeat(3)).subscribe(x => console.log(x));
// 1
// 2
// 1
// 2
// 1
// 2

对于不会结束的流 repeat 效果永远不会触发,这里的弹珠图也很简单:

image.png

repeatWhen

接收一个返回 Observable 的函数作为参数,当参数 Observable 触发时,重播已经结束的原始 Observable,这里可以看一个例子:

const documentClick$ = fromEvent(document, 'click');
of(1, 2).pipe(repeatWhen(() => documentClick$)).subscribe(x => console.log(x));

这里的条件 Observable 是文档点击事件,效果就是每点击一下就会重播 of(1, 2),打印出 1 和 2,使用弹珠图描述:

image.png

range

这个是一个比较简单的操作符,生成一组数字序列:

range(3, 5).subscribe(x => console.log(x));
// 3
// 4
// 5

弹珠图:

image.png

defer

惰性创建 Observable,传入一个函数,可以在收到订阅时新创建一个 Observable,举个例子:

const clicksOrInterval = defer(function () {
  return Math.random() > 0.5
    ? fromEvent(document, 'click')
    : interval(1000);
});
clicksOrInterval.subscribe(x => console.log(x));

这里收到订阅会判断随机数,可能是 fromEvent(document, 'click'),也可能是 interval(1000),最终订阅的目标是在执行过程中动态决定的,创建的时机被延后了。

弹珠图:

image.png

throwError

抛出一个错误,可以被 Observer 监听到:

throwError(() => {
   return new Error('error');
}).subscribe({
   error: err => console.log(err);
});

弹珠图:

image.png

ajax

发起网络请求,返回一个 Observable 对象,使用 ajax 方法可以不经过 promise 转化,直接以 Observable 的形式进行网络请求,参数可以是 url 或者整个请求对象,与直接通过 fetch API 发送网络请求的用法基本上是相同的。

bindCallback

将 callback 形式的 API 转化为 Observable:

const getJSONAsObservable = bindCallback(jQuery.getJSON);
const result = getJSONAsObservable('/my/url');
result.subscribe(x => console.log(x), e => console.error(e));

bindNodeCallback

将 nodejs 的 error first 风格 API 转化为 Observable:

const readFileAsObservable = bindNodeCallback(fs.readFile);
const result = readFileAsObservable('./roadNames.txt', 'utf8');
result.subscribe(x => console.log(x), e => console.error(e));

generate

传入规则,自定义生成一个 Observable,举个例子:

generate({
  initialState: 0,
  condition(value) { return value < 3; },
  iterate(value) { return value + 1; },
  resultSelector(value) { return value * 1000; }
}).subscribe(
  x => console.log(x)
);
// 0
// 1000
// 2000

也可以简写:

generate(0, x => x < 3, x => x + 1, x => x * 1000).subscribe(
  x => console.log(x)
);

以上这些就是常用的创建 Observable 的一些方法(严格来说有一些不算是操作符),在实际开发中获取 Observable 是最基础的一步,数据产生之后还会经历一系列其他的转换和处理,创建操作符的内容就介绍这么多。