RxJS庫

介紹

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的概念見後面)操作數據的集合。它們遵從下表:

 SingleMultiple
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"出多個值

 ProducerConsumer
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的靜態方法可以創建下面的類型

SchedulerPurpose
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



發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章