RxJS 基础(一)

如果有过响应式编程的经验,学起来应该比较容易接受一些。我们先从最基本的内容学起。 通过这一篇文章,我们可以对 RxJS 这个库有一个基本认识。

RxJS 基础(一)
Photo by Javier Garcia Chavez / Unsplash

平时我们在浏览器中如果需要为 Dom 对象绑定一个事件,那我们会写:

document.addEventListener('click', () => console.log('Clicked!'));

在 RxJS 中,我们可以以下面这种形式来达到同样的效果:

import { fromEvent } from 'rxjs';

fromEvent(document, 'click').subscribe(() => console.log('Clicked!'));

上面代码很好理解,通过 RxJS 内置的 fromEvent 事件,我们为 document 绑定了一个点击事件处理器函数,这个处理器函数会在 document 元素被点击的时候触发,从而在控制台输出 'Clicked!'

很简单对吧?下面我们通过几个代码例子来大概看一下 RxJS 究竟能帮我们做什么。

纯粹性

通过 scan 操作符函数我们可以在内部记录一个值,这个值不会被暴露在公共作用域被其他程序意外的修改掉,从而避免一些问题:

import { fromEvent } from 'rxjs';
import { scan } from 'rxjs/operators';

fromEvent(document, 'click')
  .pipe(scan(count => count + 1, 0))
  .subscribe(count => console.log(`Clicked ${count} times`));

这里我们使用了 pipe 函数,这个函数中可以指定多个操作符函数,并达到管道流式处理的效果。我们在管道流中声明 scan,并指定了一个帮我们累加值的回调。

接着,我们订阅了这个事件流,并在事件触发后输出总点击次数。

看起来很类似 Array.reduce 函数对吧?

想想这里的实现,应该是用到了闭包。在创建 scan 操作符的时候创建了一个变量,每次有新的事件触发,scan 的回调都会被调用,从而执行我们的自增逻辑,最终这个值被带入到我们的事件处理器函数中。

控制流

看下如何通过 RxJS 来实每秒钟只触发一次事件的控制逻辑。

普通情况下我们会写:

let count = 0;
let rate = 1000;
let lastClick = Date.now() - rate;
document.addEventListener('click', () => {
  if (Date.now() - lastClick >= rate) {
    console.log(`Clicked ${++count} times`);
    lastClick = Date.now();
  }
});

使用 RxJS:

import { fromEvent } from 'rxjs';
import { throttleTime, scan } from 'rxjs/operators';

fromEvent(document, 'click')
  .pipe(
    throttleTime(1000),
    scan(count => count + 1, 0)
  )
  .subscribe(count => console.log(`Clicked ${count} times`));

确实很方便,RxJS 内置了节流函数的实现,我们可以很方便的通过两个操作符函数实现同样的逻辑却不需要去关注实现细节。

RxJS 还提供了很多其他常用的操作函数如:filter, delay, debounceTime, take, takeUntil, distinct, distinctUntilChanged 等等。

值处理

我们可以通过增加一个 map 操作符函数到管道流(pipe)中来对每一次事件所产生的事件对象进行转换处理。

通常情况下我们会写:

let count = 0;
const rate = 1000;
let lastClick = Date.now() - rate;
document.addEventListener('click', event => {
  if (Date.now() - lastClick >= rate) {
    count += event.clientX;
    console.log(count);
    lastClick = Date.now();
  }
});

使用 RxJS:

import { fromEvent } from 'rxjs';
import { throttleTime, map, scan } from 'rxjs/operators';

fromEvent(document, 'click')
  .pipe(
    throttleTime(1000),
    map(event => event.clientX),
    scan((count, clientX) => count + clientX, 0)
  )
  .subscribe(count => console.log(count));

上面例子中,通过在管道中加入 map ,我们可以获取到事件对象,并将其转换为后续操作符所需要的值并返回。在 scan 中我们拿到 map 操作符返回的 clientX,并进行累加,最终值将会作为结果传给事件处理器。

RxJS 还提供了其他一些值处理的操作符 pluck, pairwise, sample 等等。

结语

通过上面几个例子,我们已经对 RxJS 有了一些基本的认识。 RxJS 在处理基于事件的代码中,为我们实现了众多的操作符。通过组合这些操作符,我们可以轻易的实现我们的业务逻辑,并且可以避免一些问题。

下面我们继续学习 Observable 对象。