Introduction 介绍

RxJS is a library for composing asynchronous and event-based programs by using observable sequences. It provides one core type, the Observable, satellite types (Observer, Schedulers, Subjects) and operators inspired by Array methods (map, filter, reduce, every, etc) to allow handling asynchronous events as collections.
RxJS 是一个通过使用可观察序列来编写异步和基于事件的程序的库。它提供了一个核心类型 Observable、卫星类型(Observer、Schedulers、Subjects)以及受 Array 方法( mapfilterreduceevery 等)启发的操作符,以允许将异步事件作为集合进行处理。

Think of RxJS as Lodash for events.
将 RxJS 视为事件的 Lodash。

ReactiveX combines the Observer pattern with the Iterator pattern and functional programming with collections to fill the need for an ideal way of managing sequences of events.
ReactiveX 将观察者模式与迭代器模式以及集合的函数式编程相结合,以满足管理事件序列的理想方式的需求。

The essential concepts in RxJS which solve async event management are:
RxJS 中用于解决异步事件管理的基本概念有:

  • Observable: represents the idea of an invokable collection of future values or events.
    可观察对象:表示未来值或事件的可调用集合的概念。
  • Observer: is a collection of callbacks that knows how to listen to values delivered by the Observable.
    观察者:是一组回调函数的集合,知道如何监听可观察对象传递的值。
  • Subscription: represents the execution of an Observable, is primarily useful for cancelling the execution.
    订阅:表示对 Observable 的执行,主要用于取消执行。
  • Operators: are pure functions that enable a functional programming style of dealing with collections with operations like map, filter, concat, reduce, etc.
    运算符:是纯函数,支持使用诸如 mapfilterconcatreduce 等操作以函数式编程风格处理集合。
  • Subject: is equivalent to an EventEmitter, and the only way of multicasting a value or event to multiple Observers.
    主题:相当于一个 EventEmitter,并且是向多个观察者多播一个值或事件的唯一方式。
  • Schedulers: are centralized dispatchers to control concurrency, allowing us to coordinate when computation happens on e.g. setTimeout or requestAnimationFrame or others.
    调度程序:是集中式的调度器,用于控制并发,使我们能够协调例如在 setTimeoutrequestAnimationFrame 或其他上何时进行计算。

First examples 首批示例

Normally you register event listeners.
通常情况下,您会注册事件监听器。

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

Using RxJS you create an observable instead.
使用 RxJS,您改为创建一个可观察对象。

import { fromEvent } from 'rxjs'; fromEvent(document, 'click').subscribe(() => console.log('Clicked!'));
      
      
      import { fromEvent } from 'rxjs';

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

Purity 纯度;纯洁;纯净

What makes RxJS powerful is its ability to produce values using pure functions. That means your code is less prone to errors.
使 RxJS 强大的是它使用纯函数生成值的能力。这意味着您的代码不太容易出错。

Normally you would create an impure function, where other pieces of your code can mess up your state.
通常情况下,您会创建一个不纯的函数,在这种函数中,您代码的其他部分可能会弄乱您的状态。

let count = 0; document.addEventListener('click', () => console.log(`Clicked ${++count} times`));
      
      
      let count = 0;
document.addEventListener('click', () => console.log(`Clicked ${++count} times`));
    

Using RxJS you isolate the state.
使用 RxJS 可以隔离状态。

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

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

The scan operator works just like reduce for arrays. It takes a value which is exposed to a callback. The returned value of the callback will then become the next value exposed the next time the callback runs.
扫描操作符对于数组的作用就像 reduce 一样。它获取一个值,并将其暴露给回调函数。回调函数的返回值随后将成为下次回调函数运行时暴露的下一个值。

Flow 流动;流量;流畅;传播

RxJS has a whole range of operators that helps you control how the events flow through your observables.
RxJS 有一系列的操作符,可帮助您控制事件如何在可观察对象中流动。

This is how you would allow at most one click per second, with plain JavaScript:
以下是如何使用纯 JavaScript 实现每秒最多点击一次:

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(); } });
      
      
      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();
  }
});
    

With RxJS: 使用 RxJS:

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

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

Other flow control operators are filter, delay, debounceTime, take, takeUntil, distinct, distinctUntilChanged etc.
其他流控制操作符有 filter、delay、debounceTime、take、takeUntil、distinct、distinctUntilChanged 等。

Values 值;价值观;数值

You can transform the values passed through your observables.
您可以转换通过可观察对象传递的值。

Here's how you can add the current mouse x position for every click, in plain JavaScript:
以下是在纯 JavaScript 中如何为每次点击添加当前鼠标的 x 位置:

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(); } });
      
      
      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();
  }
});
    

With RxJS: 使用 RxJS:

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

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

Other value producing operators are pluck, pairwise, sample etc.
其他产生值的运算符有 pluck、pairwise、sample 等。