介绍

RxJS是一个异步编程的库,同时它通过observable序列来实现基于事件的编程。它提供了一个核心的类型:Observable,几个辅助类型(Observer,Schedulers,Subjects),受到Array的扩展操作(map,filter,reduce,every等等)启发,允许直接处理异步事件的集合。

ReactiveX结合了Observer模式、Iterator模式和函数式编程和集合来构建一个管理事件序列的理想方式。

在RxJS中管理异步事件的基本概念如下:

  • Observable:代表了一个调用未来值或事件的集合的概念
  • Observer:代表了一个知道如何监听Observable传递过来的值的回调集合
  • Subscription:代表了一个可执行的Observable,主要是用于取消执行
  • Operators:是一个纯函数,允许处理集合与函数式编程风格的操作,比如map、filter、concat、flatMap等
  • Subject:相当于一个EventEmitter,它的唯一的方法是广播一个值或事件给多个Observer
  • Schedulers:是一个集中式调度程序来控制并发性,允许我们在setTimeout或者requestAnimationFrame上进行协调计算

第一个例子

正常注册一个事件监听函数:

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

使用RxJS,你可以创建一个observable来代替:

var button = document.querySelector('button');
Rx.Observable.fromEvent(button, 'click')
.subscrible(() => console.log('Clicked!'));

纯粹

使得RxJS变得如此强大的原因是它使用了纯函数,这意味着你的代码很少会发生错误。

正常你不会创建一个纯函数,代码的其他部分可能扰乱你的状态。

var count = 0;
var button = document.querySelector('button');
button.addEventListener('click', () => console.log(`Clicked $(++count) times`));

RxJS将隔离你的状态

var button = document.querySelector('button');
Rx.Observable.fromEvent(button, 'click')
.scan(count => count + 1, 0)
.subscribe(count => console.log(`Clicked ${count} items`));

scan操作符类似于arrays的reduce操作符。它需要一个回调函数作为一个参数,函数返回的值将作为下次调用时的参数。

Flow

RxJS有一系列的操作符来帮你监控事件将如何流动。

这是一个每秒最多点击一次的程序:

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

使用RxJS:

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

另外的控制符还有:filter, delay, debounceTime, take, takeUntil, distinct, distinctUntilChanged等。

你可以使用你的observables来转换值。

这是一个每次点击添加x坐标的程序:

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

使用Rxjs:

var button = document.querySelector('button');
Rx.Observable.fromEvent(button, 'click')
.throttleTime(1000)
.map(event => event.clientX)
.scan((count, clientX) => count + clientX, 0)
.subscribe(count => console.log(count));

另外的producing操作符:pluck、pairwise、sample等

Observable

Observables是一个延迟Push(关于Push的概念见后面)操作数据的集合。它们遵从下表:

  Single Multiple
Pull Function Iterator
Push Promise Observable

举个例子。下面是一个Observable,当执行subscribed,它将会立即push 1、 2、 3(同步),然后过去一秒后push 4

var observable = Rx.Observable.create(function (observer) {
observer.next(1);
observer.next(2);
observer.next(3);
setTimeout(() => {
observer.next(4);
observer.complete();
}, 1000);
});

为了调用Observable,然后看这些值,我们需要对这些数据进行订阅(subscribe)

var observable = Rx.Observable.create(function (observer){
observer.next(1);
observer.next(2);
observer.next(3);
setTimeout(() => {
observer.next(4);
observer.complete();
})
}); console.log('just before subscribe');
observerble.subscribe({
next: x => console.log(`got value` + x),
error: err => console.error('somthing wrong occurred: ' +err),
complete: () => console.log('done')
});
console.log('just after subscribe');

执行结果如下:

just before subscribe
got value 1
got value 2
got value 3
just after sbuscribe
got value 4
done

Pull和Push

PullPush是关于数据提供者和数据消费者交互的两个不同的协议。

什么是Pull?在Pull系统中,当Consumer收到Producer的数据时,它会自己判断是否接收该数据,Producer自己并不知道数据将交给哪个Consumer。

所有的JavaScript函数都是一个Pull系统。函数是一个数据提供者,调用函数的代码是一个consuming(消费者),它将函数返回的值"pulling"出来。

ES2015介绍了generator functions and iterators (function*),它们是另外一种Pull系统。iterator.next() 是Consumer,它从iterator(Producer)中"pulling"出多个值

  Producer Consumer
Pull 被动:当需要时产生数据 主动:决定是否接收数据
Push 主动:自己决定将数据传给谁 被动:响应式接收数据

什么是Push?在Push系统中,Producer决定将数据发往哪个Consumer。Consumer并不知道它自己的值来自哪个Producer

Promise是最常见的一个Push系统。一个Promise(Producer)分发一个结果值给注册的接口(Consumer),与函数不同的是,Promise当遇到值被"push"给callback时,他会保证它传递的对象是正确的。

RxJS介绍了Observables,它是一个新的Push系统。Observable是一个提供多值的Producer,将它们"pushing"给Observers(Consumer)

  • Function:计算并同步调用一个值
  • generator:计算并同步调用多个值
  • Promise:计算后可能(不可能)返回一个值
  • Observable:计算然后同步或异步返回一个或多个值

Observable as generalizations of functions

与主流相反,Observable不像EventEmitters,也不像Promise。在某些情况下,Observable的行为可能像EventEmitters,比如当使用RxJS的Subjects进行多途径传播时,但是大部分的情况它们都是不一样的。

考虑下面的情况:

function foo(){
console.log('Hello');
return 42;
} var x = foo.call(); // same as foo()
console.log(x);
var y = foo.call(); // same as foo()
console.log(y)

我们期望出现下面的结果:

"Hello"
42
"Hello"
42

当使用Observables时:

var foo = Rx.Observable.create(function (observer){
console.log('Hello');
observer.next(42);
}); foo.subscribe(function (x){
console.log(x);
});
foo.subscribe(function (y){
console.log(y);
})

它们有着同样地输出:

"Hello"
42
"Hello"
42

之所以出现这种情况是因为function和Observables都是延迟(lazy)计算的。如果你不调用function,console.log('Hello')这段代码是不会执行的。Observables是同样的,如果你不执行(subscribe)它,代码也不会执行。“calling”和"subscribing"都是一个独立的操作:两个function分别导致两个结果,两个Observale subscribes trigger也会分别导致两个结果。这与EventEmitters截然相反,EventEmitters会共享结果,并且它执行的时候也不会顾忌到底是否有subscribers存在,Observables不会是共享结果,并且也是延迟执行。

Subscribing一个Observable就像调用一个函数一样

一些人要求Observables是异步的,这是不正确的。看下面这个例子:

console.log('before');
console.log(foo.call());
console.log('after');

你将会看到这样的输出:

"before"
"Hello"
42
"after"

使用Observables

console.log('before');
foo.subscribe(function(x) {
console.log(x);
});
console.log('after');

输出是:

"before"
"Hello"
42
"after"

这证明了foo的订阅是一个完完全全的异步,就像一个函数一样。

Observables可以同步或异步地传递一个值

Observable和function的不同是什么呢?随之时间的流逝,Observables可以“返回”多个值,函数是不可以的。你不可以这么做:

function foo(){
console.log('Hello');
return 42;
return 100; // 不会执行到这儿
}

函数只能返回一次,Observables可以做到返回多次:

var foo = Rx.Observable.create(function (observer){
console.log('Hello');
observer.next(42);
observer.next(100); // "return another value"
observer.next(200); // "return" yet another
}); console.log('before');
foo.subscribe(function (x){
console.log(x);
});
console.log('after');

同步输出:

"before"
"Hello"
42
100
200
"after"

你也可以异步返回:

var foo = Rx.Observable.create(function (observer){
console.log('Hello');
observer.next(42);
observer.next(100);
observer.next(200);
setTimeout(() => {
observer.next(300); // 异步
}, 1000);
}); console.log('before');
foo.subscribe(function(x){
console.log(x);
});
console.log('after');

输出:

"before"
"Hello"
42
100
200
"after"
300

结论:

  • func.call()表示“同步给我一个数据”
  • observable.subscribe()表示“给我任何数量的值,同步或者异步”

解析一个Observable

Observables使用Rx.Observable.create或者一个构造器创建(create),使用Observer来监听(subscribed),执行(execute)是通过投递一个next/error/complete来通知其他的Observer,然后按照各自的意愿(disposed)来执行。在一个Observable实例中,这四个方面都是通过编码实现的,但是这些可能与其他的类型相关,比如Obsrever和Subscription。

Observable的核心点:

  • Creating Observables
  • Subscribing to Observables
  • Executing the Observable
  • Disposing Observables

创建一个Observables

Rx.Observable.create是Observable构造器的一个别名,他需要一个参数:一个subscribe函数

下面的例子创建一个Observable,它的作用是每秒钟输出字符串'hi':

var observable = Rx.Observable.create(function subscrite(observer){
var id = setInterval(() => {
observer.next('hi')
}, 1000);
});

Observables可以使用create创建,但是我们经常使用creation operators,诸如from,interval等。

在上面的例子中,subscribe函数是描述Observable最重要的一部分,让我们来看看subscribing是什么意思。

subscribing to Observables

Observable的observable可以被订阅(subscribed),就像这样:

observable.subscribe(x => console.log(x));

observable.scribe和Observable.create(function subscribe(observer) {...})中的subscribe有着相同的名字并不是巧合。在库中,它们是不同的,但是在实际的用途中你可以在逻辑上把他们想成相同的。

同样的Observable被多个Observers监听时,它们是不共享的。

Subscribing一个Observable像调用一个函数一样,当一个数据被传递时提供一个回调

这个addEventListener/removeEventListener这样的API完全不一样。observable.subscribe作为一个给定的观察者,在Observable中并没有像listener一样被注册。Observable甚至不需要维护一系列的Observers。

Executing observables

代码Observable.create(function subscribe(observer) {...})代表了一个"Observable execution",它将仅仅在每个Observer的subscribes的延迟计算中。随着时间的推移,将产生多个结果,同步或者异步。

Observable可以传递的有三种类型:

  • "Next" notification:传递一个数值,诸如Number、String、Object等
  • “Error” notification:传递一个js异常
  • "Complete" notification:什么值都不传递

Next notifications是最重要的也是最常见的类型:它们表示一个实际数据被送到Observer。在Observable Execute执行期间Error和Complete最多会发生一次。

下面的语法是在Observable Grammar or Contract中最好的表达:

next*(error|complete)?

在一个Observable Execute中,0或多个Next notifications可能被传递。如果有error或者Complete被传递,剩下的next将不会被传递。

下面是Observable execute传递3个Next notifications的例子:

var observable = Rx.Observable.create(function subscribe(observer) {
observer.next(1);
observer.next(2);
observer.next(3);
observer.complete();
})

下面的例子中,Next notification 4不会被传递:

var observable = Rx.Observable.create(function subscribe(observer){
observer.next(1);
observer.next(2);
observer.next(3);
observer.complete();
observer.next(4); // 不会被执行
})

用tru/catch代码快包裹起来是个好主意:

var observable = Rx.Observable.create(function subscribe(observer) {
try {
observer.next(1);
observer.next(2);
observer.next(3);
observer.complete();
} catch (err) {
observer.error(err); // delivers an error if it caught one
}
});

处理(Disposing)Observable Executions

Observable Executing的个数可能是无限个,Observer中应该处理有限个next,所以我们需要一个API来停止execution。因为execution在每个Observer中都是独立的,一旦Observer完成接收值,它必须有一个方法来停止executing。

当 observable.subscribe 被调用,Observer将被附加到一个新创建的Observable execution中,这次调用将返回一个对象,即Subscription:

var subscription = observable.subscribe(x => console.log(x));

Subscription代表了一个进行中的executing,它有一个最小的API允许你取消execution。可以在这里阅读更多有关于 Subscription type here 的东西。使用 subscription.unsubscribe() 你可以取消正在进行的execution:

var observable = Rx.Observable.from([10, 20, 30]);
var subscription = observable.subscribe(x => console.log(x));
// Later:
subscription.unsubscribe();

当我们使用create()创建一个Observable时,我们必须定义execution怎么处理资源。你可以通过返回一个自定义的 unsubscribe 函数来实现该步骤。

var observable = Rx.Observable.create(function subscribe(observer){
var intervalID = setInterval(() => {
observer.next('hi')
}); return function unsubscribe(){
clearInterval(intervalID);
}
})

然后这样来调用:

function subscribe(observer) {
var intervalID = setInterval(() => {
observer.next('hi');
}, 1000); return function unsubscribe() {
clearInterval(intervalID);
};
} var unsubscribe = subscribe({next: (x) => console.log(x)}); // Later:
unsubscribe(); // dispose the resources

Observer

什么是Observer?一个Observer是Observable传递过来的数据的customer。Observers是一个简单的一些列的回调,next、error、和 complete用来传递数据。下面的例子展现了一个典型的Observer对象:

var observer = {
next: x => console.log('Observable got a next value: ' + x),
error: err => console.log('Observable got and error: ' + err),
complete: () => console.log('Observable got a complete notification')
};

为了使用Observalbe,提供了一个subscribe:

observable.subscribe(observer)

你也可以提供部分回调:

var observer = {
next: x => console.log('Observer got a next value: ' + x),
error: err => console.error('Observer got an error: ' + err),
};

当你订阅(subscribing)一个Observable时,你也许仅仅只提供一个函数作为参数:

observable.subscribe(x => console.log('Observer got a next value: ' + x));

在observable.subscribe的内部,他将使用第一个回调创建一个Observer对象作为一个next handler。所有的callback类型都可能被提供:

observable.subscribe(
x => console.log('Observer got a next value: ' + x),
err => console.error('Observer got an error: ' + err),
() => console.log('Observer got a complete notification')
);

Subscription

什么是Subscription?一个Subscription代表了一个一次性的资源,通常表示的是一个Observable execution。一个Subscription有一个重要的方法,unsubscribe,它不需要参数,仅仅是处理subscription的资源。在之前的RxJS版本中,Subscription被称作"Disposable"。

var observable = Rx.Observable.interval(1000);
var subscription = observable.subscribe(x => console.log(x));
// Later:
// This cancels the ongoing Observable execution which
// was started by calling subscribe with an Observer.
subscription.unsubscribe();

一个Subscription实质上是一个unsubscribe()函数,用来释放资源或者取消一个Observable executions。

Subscriptions也可以放在一起,这样会导致使用一个unsubscribe()将取消多个Observable executions。你可以这么做:

var observable1 = Rx.Observable.interval(400);
var observable2 = Rx.Observable.interval(300); var subscription = observable1.subscribe(x => console.log('first: ' + x));
var childSubscription = observable2.subscribe(x => console.log('second: ' + x)); subscription.add(childSubscription); setTimeout(() => {
// Unsubscribes BOTH subscription and childSubscription
subscription.unsubscribe();
}, 1000);

当执行时,我们将看到如下输出:

second: 0
first: 0
second: 1
first: 1
second: 2

Subscriptions有一个remove(otherSubscription)方法,用来移除关联的Subscirption

Subject

什么是Subject?一个RxJS Subject是一个特殊类型的Observable,它允许值可以多路广播给多个Observers。普通的Observables是单路广播(每个subscribed Observer拥有自己独立的Observable execution),Subjects是多路广播。

一个Subject像一个Observable,但是可以多路广播给Observers。Subjects像Eventmitters:它们维持许多注册过的监听器。

每个subject是一个Observable。给定一个Subject,你可以通过提供一个Observer来订阅(subscribe)它,然后开始正常的接收值。从Observer的角度来看,他不能告知Observer的Observable execution到底是来自一个不同的单路传播的Observable,还是来自Subject。

在Subject的内部,subscribe并没有调用一个新的execute去传递数据。它只是简单的注册Observers列表中的一个Observer,类似于addListener的使用。

每个subject是一个Observer。他是拥有next(v),error(e)和complete()方法的对象。为了给Subject一个新值,只需要调用 next(theValue),他讲多路传播给注册过的Observer。

在下面的例子中,我们在Subject中注册了两个Observers,我们传递一些值给Subject:

var subject = new Rx.Subject();

subject.subscribe({
next: (v) => console.log('observerA: ' + v)
});
subject.subscribe({
next: (v) => console.log('observerB: ' + v)
}); subject.next(1);
subject.next(2);

输出:

observerA: 1
observerB: 1
observerA: 2
observerB: 2

因为Subject同时也是一个Observer,这意味着你应该提供一个Subject作为Observable的subscribe的参数,像这样:

var subject = new Rx.Subject();

subject.subscribe({
next: (v) => console.log('observerA: ' + v)
});
subject.subscribe({
next: (v) => console.log('observerB: ' + v)
}); var observable = Rx.Observable.from([1, 2, 3]); observable.subscribe(subject); // You can subscribe providing a Subject

执行如下:

observerA: 1
observerB: 1
observerA: 2
observerB: 2
observerA: 3
observerB: 3

在上面的处理中,我们本质上仅仅是通过Subject将一个单路广播的Observable execution变为多路广播的。这个演示展示了Subjects是怎样将单路广播变为多路广播的。

这里有几个特殊的Subject类型:BehaviorSubject、ReplaySubject和AsyncSubject。

Multicasted Observables

一个"multicasted Observable"的实现是通过Subject的多个订阅(subscribers)来实现的,然而一个"unicast Observable"仅仅只通知一个单一的Observer。

在后台,multicast是这样操作的:Observers订阅(subscribe)一个相关的Subject,Subject订阅一个Observable源。

var source = Rx.Observable.from([1, 2, 3]);
var subject = new Rx.Subject();
var multicasted = source.multicast(subject); // These are, under the hood, `subject.subscribe({...})`:
multicasted.subscribe({
next: (v) => console.log('observerAa: ' + v)
});
muticasted.subscribe({
next: (v) => console.log('observerB: ' + v)
}); // This is, under the hood, `source.subscribe(subject)`:
muticasted.connect();

Reference counting

手动的调用connect()来处理Subscription是很麻烦的。通常,我们希望当第一个Observer到达时,能够自动connect,当最后一个Observer被移除时,自动取消shared execution。

看看下面这些订阅发生时的列表:

  1. 第一个Observer订阅multicasted Observable
  2. multicasted observable连接
  3. next value 0被传递给第一个Observer
  4. 第二个Observer订阅multicasted Observable
  5. next value 1被传递给第一个Observer
  6. next value 1被传递给第二个Observer
  7. 第一个Observer解除监听
  8. next value2被传递给第二个Observer
  9. 第二个Observer解除监听
  10. 与multicasted observable连接的Observable解除连接

看下面的代码:

var source = Rx.Observable.interval(500);
var subject = new Rx.Subject();
var multicasted = source.multicast(subject);
var subscription1, subscription2, subscriptionConnect; subscription1 = multicasted.subscribe({
next: (v) => console.log('observerA: ' + v)
});
// We should call `connect()` here, because the first
// subscriber to `multicasted` is interested in consuming values
subscriptionConnect = multicasted.connect(); setTimeout(() => {
subscription2 = multicasted.subscribe({
next: v => console.log('observerB: ' + v)
});
}, 600); setTimeout(() => {
subscrption1.unscribe();
}, 1200); // We should unsubscribe the shared Observable execution here,
// because `multicasted` would have no more subscribers after this
setTimeout(() => {
subscription2.unsubscribe();
subscriptionConnect.unsubscribe(); // for the shared Observable execution
}, 2000);

如果我们希望避免一遍遍地调用connect(),我们可以使用ConnectableObservable的refCount()方法(reference counting),它返回一个Observable来跟踪有多少个订阅者(subscribers)。当订阅者从0增加到1时,它将自动调用connect(),只有当订阅者从1变为0时,它才会disconnect。

看下面的例子:

var source = Rx.Observable.interval(500);
var subject = new Rx.Subject();
var refCounted = source.multicast(subject).refCount();
var subscrption1, subscription2, subscriptionConnect; // This calls `connect()`, because
// it is the first subscriber to `refCounted`
console.log('observerA subscribed');
subscription1 = refCounted.subscribe({
next: (v) => console.log('observerA: ' + v);
}); setTimeout(() => {
console.log('observerB subscribed');
subscription2 = refCounted.subscribe({
next: (v) => console.log('observerA: ' + v)
});
}, 600); setTimeout(() => {
console.log('observerA unsubscribed');
subscription1.unsubscribe();
}, 1200); // This is when the shared Observable execution will stop, because
// `refCounted` would have no more subscribers after this
setTimeout(() => {
console.log('observerB unsubscribed');
subscription2.unsubscribe();
}, 2000);

执行结果:

observerA subscribed
observerA: 0
observerB subscribed
observerA: 1
observerB: 1
observerA unsubscribed
observerB: 2
observerB unsubscribed

refCount()方法仅存在ConnectableObservable中,它返回一个Observable,而不是另外的ConnectableObservable。

BehaviorSubject

Subjects的一种变形是BehaviorSubject,它有一个"the current value" 的概念。它存储了consumer最后一次执行时的value,每当一个Observer订阅时,它都会立即从BehaviorSubject接收一个"current value"。

例子:

var subject = new Rx.BehaviorSubject(0);  //  0 is the inital value

subject.subscribe({
next: v => console.log('observerA: ' + v)
}); subject.next(1);
subject.next(2); subject.subscribe({
next: v = console.log('observerB: ' + v)
}); subject.next(3);

输出:

observerA: 0
observerA: 1
observerA: 2
observerB: 2
observerA: 3
observerB: 3

ReplaySubject

功能和它的名字一样:

var subject = new Rx.ReplaySubject(3);  // buffer 3 values for new subscribers

subject.subscribe({
next: v => console.log('observerA: ' + v)
}); subject.next(1);
subject.next(2);
subject.next(3);
subject.next(4); subject.subscribe({
next: v => console.log('observerB: ' + v)
}); subject.next(5);

输出:

observerA: 1
observerA: 2
observerA: 3
observerA: 4
observerB: 2
observerB: 3
observerB: 4
observerA: 5
observerB: 5

你还可以指定一个以毫秒为单位的窗口事时间,除了buffer size之外,决定记录的值可以重复(时间内)。

var subject = new Rx.ReplaySubject(100, 500);

subject.subscribe({
next: (v) => console.log('observerA: ' + v)
}); var i = 1;
setInterval(() => subject.next(i++), 200); setTimeout(() => {
subject.subscribe({
next: v => console.log('observerB: ' + v)
});
}, 1000)

下面的输出中,第二个Observer在最后500ms内得到的数值为3、 4、 5:

observerA: 1
observerA: 2
observerA: 3
observerA: 4
observerA: 5
observerB: 3
observerB: 4
observerB: 5
observerA: 6
observerB: 6
...

AsyncSubject

AsyncSubject表示只有最后一个Observable execution的值会被发送给observers,仅仅发生在执行完成时

var subject = new Rx.AsyncSubject();

subject.subscrbe({
next: v => console.log('onbserverA: ' + v)
}); subject.next(1);
subject.next(2);
subject.next(3);
subject.next(4); subject.subscribe({
next: (v) => console.log('observerB: ' + v)
}); subject.next(5);
subject.complete();

输出:

observerA: 5
observerB: 5

AsyncSubject类似于一个last() operator,他等待complete通知来传递一个唯一的值。

Opeartors

RxJS最有用的一部分就是operators,即使Observable是最基础的。Operators最基本的要点是允许复杂的代码变得简单化。

什么是Operators?

Opeartors是Obsrevable的方法,就像map(),filter(),merge()等。当它被调用时,它们并不改变已经存在的Observable,而是返回一个基于第一个Observable上新的Observable。

一个Operator本质上是一个纯函数,它接收一个Observable,基于其上返回一个新的Observable。在下面的例子中,我们创建了一个自定义的operator方法:

function multiplayByTen(input){
var output = Rx.Observable.create(function subscribe(observer){
input.subscribe({
next: v => observer.next(10 * v),
error: err => observer.error(err),
complete: () => observer.complete()
});
});
return output;
} var input = Rx.Observable.from([1, 2, 3 ,4]);
var output = multiplayByTen(input);
output.subscribe(x => console.log(x));

输出为:

10
20
30
40

注意订阅(subscribe)的输出将导致输入的Observable可观测的变化。我们称这个为"operator subscription chain"。

Instance opeartors versus static operators

什么是instance operator?最常见的情况是当你引用一个opeartors时,我们假定实现了一个operator,它是Observable实例的一个方法。例如,如果multiplayByTen operator变成一个官方的operator,它看起来会是这样:

Rx.Observable.prototype.multiplyByTen = function multiplyByTen(){
var input = this;
return Rx.subscrible.create(function subscribe(observer){
input.subccribe({
next: (v) => observer.next(10 * v),
error: (err) => observer.error(err),
complete: () => observer.complete()
});
});
}

Instance operators是一个实例运算符,我们使用它来推断可观测的输入。

注意,input observable不再是一个函数参数:

var observable = Rx.Observable.from([1, 2, 3, 4]).multiplyByTen();
observable.subscribe(x => console.log(x));

什么是static operator?除了instance operators之外,static operators是直接附加在Observable类上的方法。一个static operator使用内部的this进行操作,但是并不完全依赖它的参数。

static operators是附着在Observable类上的纯函数,通常用于创建Observable

最常见的static operators类型是Create Operators,他不是将一个Observable改变成另外一个Observable,它们简单的获得一个non-Observable参数,比如number,然后create一个新的Observable。

一个典型的例子是使用interval函数。它获取一个数值(不是一个Observable)作为输入参数,然后输出一个Observable:

var observable = Rx.Observable.interval(1000 /* number of milliseconds */)

创建一个creation operaor的另外一个例子是create,就是我们之前一直在使用的例子。 all static creation operators here

然而,static operators也许和普通的creation性质不同。一些Combination Operator也许是静态的,比如merge、combineLatest、concat等。将这些作为静态是有意义的,因为它们把multiple Observales作为输入,不只是一个,比如:

var observable1 = Rx.Observable.interval(1000);
var observable2 = Rx.Observable.interval(400); var merged = Rx.Observable.merge(observable1, observable2);

Marble diagrams

为了解释operators是如何工作的,光是文本解释是不够的。许多operators和时间有关,它们可能会延迟执行,例如,throttle等。图标往往能够比文字更多表达清楚。Marble Diagrams能够可视化的表现出operators是如何工作的,包括输入的Observable(s),operator和它的参数,以及输出的Observable

在一个marble diagram中,随着时间的流逝,它会描述值("marbles")在Observable execution上传递。

你可以在下面看到marble diagram的解析:

 
Paste_Image.png
  • 时间从左往右流动,代表input Observable的execution
  • 这些代表Observable传递传来的值
  • 这个竖线表示"complete" notification,它表明Observable已经成功完成了。
  • 这个方框表示input Observable的operator(上图)产生出的output Observable(下图)。方框内的文字表示转变的属性。
  • 这个Observable是调用operator产生的
  • 这个X代表output Observable发出的错误,说明因为某些原因而异常终止。

在这个网站的站点,我们会广泛的使用marble diagrams去解释operators是如何工作的。它们也许在其他的地方也很有用,比如单元测试等。

选择一个operator

你需要为你的程序选择一个适当的operator吗?先从下面的列表选择一个:

  • 我已经有了一个Observable
  • 我想改变每个传递的值
    • 让它成为一个固定(constant)的值

    • 通过公式计算出来的值
      • 你应该使用map
  • 我想选择每个传递值的属性
  • 我想查看每个被传递的值,但是不影响它们
    • 你应该使用do
  • 我想过滤某些值
    • 基于一个自定义的逻辑

更多内容参考官网:Choose an operator

operators的分类

参考官网:Categories of operators

Scheduler

什么是Scheduler?当一个subscription开始工作或者notifications被传递,scheduler就会开始调图。它包含三个组件。

  • 一个Scheduler是一个数据结构(data structure)。它知道如何基于优先级或者其它标准进行存储,执行队列任务
  • 一个Scheduler是一个执行上下文(execution context)。它表示task在哪个地方,什么时候执行()
  • 一个Scheduler是一个(虚拟(virtual))时钟。它基于scheduler上的getter方法now(),提出了一个"时间(time)"的概念。任务被安排在一个特殊的调度器中,它会遵守给它的时间。

看下面例子中,我们使用之前已经写过的例子,同步传递数值1、2、 3,然后使用observerOn操作符来指定异步调度:

var observable = Rx.Observable.create(function (observer) {
observer.next(1);
observer.next(2);
observer.next(3);
observer.complete();
})
.observerOn(Rx.Scheduler.async); 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
just after subscribe
got value 1
got value 2
got value 3
done

注意got value这个语句实在 just after subscribe只有打印输出的,这和我们看到的代码顺序不一致。这时因为 observerOn(Rx.Scheduler.async)在Observable.create和最后一个Observer之间引入了一个代理的Observer。让我们重新为一些标识符取名,以便让他们之间有着明显的差别:

var observable = Rx.Observable.create(function (proxyObserver) {
proxyObserver.next(1);
proxyObserver.next(2);
proxyObserver.next(3);
proxyObserver.complete();
})
.observeOn(Rx.Scheduler.async); var finalObserver = {
next: x => console.log('got value ' + x),
error: err => console.error('something wrong occurred: ' + err),
complete: () => console.log('done')
}; console.log('just before subscribe');
observable.subscribe(finalObserver);
console.log('just after subscribe');

proxyObserver在observeOn(Rx.Scheduler.async)中创建,它的next(val)方法大概像下面这样:

var proxyObserver = {
next: (val) => {
Rx.Scheduler.async.schedule(
(x) => finalObserver.next(x),
0 /* delay */,
val /* will be the x for the function above */
);
}, // ...
}

这有点儿像setTimeout或者setInterval是异步调度操作,即使给定的delay为0。按照惯例,在JS中,setTimeout(fn, 0)知道运行fn函数的时机最早是下一次循环队列初。这也说明了为什么 got value 1是最后运行的。

可以给Scheduler的schedule传递一个延时(delay)参数,它可以让Scheduler内部的时钟去延时到指定时间。Scheduler的时钟和真实的时钟没有任何关系。它更类似于延时,而不是运行指定的时间。

Scheduler类型

异步Scheduler只是RxJS提供的一种Scheduler。通过使用Scheduler的静态方法可以创建下面的类型

Scheduler Purpose
null 不使用Scheduler, notifications将会被同步和递归地交付给Observer。使用这个来进行常量操作或者尾部递归操作
Rx.Scheduler.queue Schedules on a queue in the current event frame (trampoline scheduler). Use this for iteration operations.
Rx.Scheduler.asap Schedules on the micro task queue, which uses the fastest transport mechanism available, either Node.js' process.nextTick() or Web Worker MessageChannel or setTimeout or others. Use this for asynchronous conversions.
Rx.Scheduler.async Schedules work with setInterval. Use this for time-based operations.

使用Schedulers

Using Schedulers

RxJS库的更多相关文章

  1. Angular基础(八) Observable & RxJS

    对于一个应用来说,获取数据的方法可以有很多,比如:Ajax, Websockets, LocalStorage, Indexdb, Service Workers,但是如何整合多种数据源.如何避免BU ...

  2. Angular快速学习笔记(4) -- Observable与RxJS

    介绍RxJS前,先介绍Observable 可观察对象(Observable) 可观察对象支持在应用中的发布者和订阅者之间传递消息. 可观察对象可以发送多个任意类型的值 -- 字面量.消息.事件. 基 ...

  3. Angular18 RXJS

    1 RX 全称是 Reactive Extensions,它是微软开发并维护的基于 Reactive Programming 范式实现的一套工具库集合:RX结合了观察者模式.迭代器模式.函数式编程来管 ...

  4. Rxjs 操作符

    1. javascript解决异步编程方案 解决javascript异步编程方案有两种,一种是promise对象形式,还有一种是是Rxjs库形式,Rxjs相对于Promise来说,有好多Promise ...

  5. [译]Angular2 和TypeScript -- 一次简要的预览

    原文链接:https://www.infoq.com/articles/Angular2-TypeScript-High-Level-Overview 作者:  Yakov Fain Posted o ...

  6. Angular2 Http

    1. 使用Http 绝大部分应用程序都会和后台服务打交道,Http是浏览器和服务器之间通讯的主要协议,通过Http调用来访问远程服务器上相应的 Web API. HttpModule 并不是 Angu ...

  7. angularjs2 学习笔记(五) http服务

    angular2的http服务是用于从后台程序获取或更新数据的一种机制,通常情况我们需要将与后台交换数据的模块做出angular服务,利用http获取更新后台数据,angular使用http的get或 ...

  8. Angular2 和TypeScript

    Angular2 和TypeScript 原文链接:https://www.infoq.com/articles/Angular2-TypeScript-High-Level-Overview 作者: ...

  9. Angular 4+ Http

    HTTP: 使应用能够对远端服务器发起相应的Http调用: 你要知道: HttpModule并不是Angular的核心模块,它是Angualr用来进行Web访问的一种可选方式,并位于一个名叫@angu ...

随机推荐

  1. (zxing.net)一维码UPC A的简介、实现与解码

    一.简介 UPC(Universal Product Code)码是最早大规模应用的条码,其特性是一种长度固定.连续性的条  码,目前主要在美国和加拿大使用,由于其应用范围广泛,故又被称万用条码. U ...

  2. Android Bug BaseExpandableListAdapter, getChildView

    @Override public View getChildView(final int groupPosition, final int childPosition, boolean isLastC ...

  3. Http协议中关于Content-Length的解读【出现坑爹的视频中断】

    最近公司的视频设备在下载视频的时候,出现了很诡异的现象,在新旧服务器一样的tpp包,下载下来后,新服务器无法解析,旧服务器没问题.且tpp包并没有改动. 后面找了挺久,终于发现了视频下载的时候是断点续 ...

  4. [agc006f] Blackout 神题

    Description ​ 给你一个NN行NN列的网格,第ii行第jj列的格子用(i,j)(i,j)表示 一开始的时候有MM个格子被涂成黑色,其他的格子都是白色,具体一点,涂成黑色的格子为(a1,b1 ...

  5. 洛谷P5292 [HNOI2019]校园旅行(二分图+最短路)

    题面 传送门 题解 如果暴力的话,我们可以把所有的二元组全都扔进一个队列里,然后每次往两边更新同色点,这样的话复杂度是\(O(m^2)\) 怎么优化呢? 对于一个同色联通块,如果它是一个二分图,我们只 ...

  6. Writing analyzers

    Writing analyzers There are times when you would like to analyze text in a bespoke fashion, either b ...

  7. angular核心原理解析2:注入器的创建和使用

    上一课没有讲到创建注入器的方法createInjector. 此方法,会创建两种不同的注入器:第一种叫做providerInjector,第二种叫做instanceInjector.providerI ...

  8. MyEclipse生成Javadoc帮助文档

    Javadoc是Sun公司提供的一个技术,它从程序源代码中抽取类.方法.成员等注释形成一个和源代码配套的API帮助文档.也就是说,只要在编写程序时以一套特定的标签作注释,在程序编写完成后,通过Java ...

  9. 图的最短路径---迪杰斯特拉(Dijkstra)算法浅析

    什么是最短路径 在网图和非网图中,最短路径的含义是不一样的.对于非网图没有边上的权值,所谓的最短路径,其实就是指两顶点之间经过的边数最少的路径. 对于网图,最短路径就是指两顶点之间经过的边上权值之和最 ...

  10. vue+ivew-admin开发项目,内存占用过大解决办法

    项目用的ivew+admin ivewUI,直接从github上拉下来用的,配置也没改,我们页面比较多,大该30个页面的样子,一启用,我们的电脑就卡了,然后,看一下,内存占用 1.5G+了,我们电脑4 ...