rxjs學習入門心得(一)Observable可觀察對象

推薦我的Rxjs教程:Rxjs系列教程目錄

前言

隨著開發(fā)中項目的越來越大,代碼的要求越來越高,于是開始四處搜找各種js庫進行學習。為了學習代碼行為模式,例如:競爭等等。在技術總監(jiān)的指引下找到Rxjs進行學習,再次表以感謝。在看教程時,有很多地方不解,于是用博客做以記錄,并將自己的經(jīng)驗以盡可能簡單的方式分享給大家。

這里簡單解釋一下Rxjs,RxJS 是一個js庫,它通過使用 observable 序列來編寫異步和基于事件的程序。ReactiveX 結合了 觀察者模式、迭代器模式 和 使用集合的函數(shù)式編程,以滿足以一種理想方式來管理事件序列所需要的一切??吹竭@你肯定疑問它有什么用?你先放下這個疑問,先看看一個簡單的案例。

Observable可觀察對象

Observable可觀察對象:表示一個可調用的未來值或者事件的集合。

一個例子

通常你這樣注冊事件監(jiān)聽:

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

使用RxJS創(chuàng)建一個可觀察對象:

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

看到這段代碼你可能迷茫,這是什么意思?難道只是換了一種寫法?

觀察者模式

要說Observable可觀察對象首先得說說:觀察者模式。

觀察者模式又叫發(fā)布-訂閱(Publish/Subscribe)模式

他定義了一種一對多的依賴關系,讓多個觀察者對象同時監(jiān)聽某一個主題對象(也可叫做抽象的通知者)。這個主題對象在狀態(tài)發(fā)生變化時,會通知所有觀察者對象,使他們能夠自動更新自己。而且各個觀察者之間相互獨立。

觀察者模式的結構中包含四種角色:

(1)抽象主題(Subject):主題是一個接口,該接口規(guī)定了具體主題需要實現(xiàn)的方法,比如,添加、刪除觀察者以及通知觀察者更新數(shù)據(jù)的方法。
(2)抽象觀察者(Observer):觀察者是一個接口,該接口規(guī)定了具體觀察者用來更新數(shù)據(jù)的方法。
(3)具體主題(ConcreteSubject):具體主題是實現(xiàn)主題接口類的一個實例,該實例包含有可以經(jīng)常發(fā)生變化的數(shù)據(jù)。具體主題需使用一個集合,比如ArrayList,存放觀察者的引用,以便數(shù)據(jù)變化時通知具體觀察者。
(4)具體觀察者(ConcreteObserver):具體觀察者是實現(xiàn)觀察者接口類的一個實例。具體觀察者包含有可以存放具體主題引用的主題接口變量,以便具體觀察者讓具體主題將自己的引用添加到具體主題的集合中,使自己成為它的觀察者,或讓這個具體主題將自己從具體主題的集合中刪除,使自己不再是它的觀察者。

觀察者模式結構的類圖如下所示:

觀察者模式結構的類圖

在現(xiàn)實生活中,我們經(jīng)常用它來“放風”,比如:上自習時,老師不在我們在玩,派出一個同學看老師,老師來了通知大家;如果該同學沒有發(fā)現(xiàn)老師,老師“咳咳”兩聲通知大家自己來了讓大家安靜自習,然后批評一番。

這里的監(jiān)聽的抽象主題對象是“老師是否來了”,同學們是觀察者,同學們依賴主題對象的狀態(tài)并且是一種一對多的依賴關系,同學們同時監(jiān)聽主題對象的反饋結果,同學們訂閱(觀察)這個主題,在這個主題發(fā)生變化時,來更新自己:

老師來了->安靜自習,寫作業(yè)。
老師沒來->該玩玩,該吃吃。

放風的同學發(fā)現(xiàn)老師會通知,放風的同學沒發(fā)現(xiàn),老師進入教室老師自己也會“咳咳”兩聲通知。因此“老師是否來了”這個抽象主題中,老師通知和放風的同學通知都是這個抽象主題對象的具體實現(xiàn),這個抽象主題對象就是可觀察對象(可以觀察嘛~~~),這時再想想前面對于可觀察對象的定義(可調用的未來值或者事件的集合)是不是明白了?

同樣上面的RxJS的代碼也是這種效應,Rx.Observable.fromEvent(button, 'click')是一個創(chuàng)建一個點擊事件的可觀察對象,然后使用subscribe(() => console.log('Clicked!'));訂閱這個可觀察對象,其中() => console.log('Clicked!')是一個觀察者,如果發(fā)生點擊事件主題對象的狀態(tài)會發(fā)生改變,而他則會被執(zhí)行。

這樣有什么好處呢?

  1. 我們只需要針對可觀察對象這一抽象的主題對象接口編程,減少了與具體的耦合,即他只是一個抽象的通知者。
  2. 觀察者只依賴主題對象的狀態(tài),這意味著維持各個觀察者的一致性,但又保證了各個觀察者是相互獨立的。

發(fā)布-訂閱

Observables(可觀察對象)以惰性的方式推送多值的集合。

示例 - 當訂閱下面代碼中的 Observable 的時候會立即(同步地)推送值1、2、3,然后1秒后會推送值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 并看到這些值,我們需要訂閱 Observable:

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
got value 1
got value 2
got value 3
just after subscribe
got value 4
done

拉取 (Pull) vs. 推送 (Push)

拉取和推送是兩種不同的協(xié)議,用來描述數(shù)據(jù)生產(chǎn)者 (Producer)如何與數(shù)據(jù)消費者 (Consumer)如何進行通信的。

什么是拉取? - 在拉取體系中,數(shù)據(jù)的消費者決定何時從數(shù)據(jù)生產(chǎn)者那里獲取數(shù)據(jù),而數(shù)據(jù)生產(chǎn)者自身并不會意識到什么時候數(shù)據(jù)將會被發(fā)送給數(shù)據(jù)消費者。

每個 JavaScript 函數(shù)都是拉取體系。函數(shù)是數(shù)據(jù)的生產(chǎn)者,調用該函數(shù)的代碼通過從函數(shù)調用中“取出”一個單個返回值來對該函數(shù)進行消費(return 語句)。

ES2015 引入了生成器generator 函數(shù) 和 迭代器iterators (function*),這是另外一種類型的拉取體系。調用iterator.next()的代碼是消費者,它會從 iterator(生產(chǎn)者) 那“取出”多個值。

什么是推送? -在推體系中,數(shù)據(jù)的生產(chǎn)者決定何時發(fā)送數(shù)據(jù)給消費者,消費者不會在接收數(shù)據(jù)之前意識到它將要接收這個數(shù)據(jù)。

Promise(承諾)是當今JS中最常見的Push推體系,一個Promise(數(shù)據(jù)的生產(chǎn)者)發(fā)送一個resolved value(成功狀態(tài)的值)來注冊一個回調(數(shù)據(jù)消費者),但是不同于函數(shù)的地方的是:Promise決定著何時數(shù)據(jù)才被推送至這個回調函數(shù)。

RxJS引入了Observables(可觀察對象),一個新的 JavaScript 推送體系。一個可觀察對象是一個產(chǎn)生多值的生產(chǎn)者,并將值“推送”給觀察者(消費者)。

Function 是惰性的評估運算,調用時會同步地返回一個單一值。
Generator(生成器):是惰性的評估運算,在迭代時同步的返回零到無限多個值(如果有可能的話)
Promise 是最終可能(或可能不)返回單個值的運算。
Observable 是惰性的評估運算,它可以從它被調用的時刻起同步或異步地返回零到(有可能的)無限多個值。

producer(生產(chǎn)者) consumer (消費者) 單個值 多個值
pull拉 Passive(被動的一方):被請求的時候產(chǎn)生數(shù)據(jù) Active(起主導的一方):決定何時請求數(shù)據(jù) Function Iterator
push推 Active:按自己的節(jié)奏生產(chǎn)數(shù)據(jù) Passive:對接收的數(shù)據(jù)做出反應(處理接收到的數(shù)據(jù)) Promise Observable

可觀察對象(Observables):作為函數(shù)的泛化

與常見的主張相悖的是,可觀察對象不像EventEmitters(事件驅動),也不象Promises因為它可以返回多個值??捎^察對象可能會在某些情況下有點像EventEmitters(事件驅動),也即是當它們使用Subjects被多播時,但是大多數(shù)情況下,并不像EventEmitters。

可觀察對象(Observables)像是沒有參數(shù), 但可以泛化為允許返回多個值的函數(shù)。

思考下面的程序

function foo() {
    console.log('Hello');
    return 42;
}
var x = foo.call(); // same as foo()
console.log(x); // "Hello" 42
var y = foo.call(); // same as foo()
console.log(y); // "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

這是因為函數(shù)和可觀察對象均是惰性計算。

如果你不調用call()函數(shù),console.log('Hello')將不會發(fā)生??捎^察對象同樣如此,如果你不調用subscribe()函數(shù)訂閱,console.log('Hello')也將不會發(fā)生。

此外,call()或者subscribe()是一個獨立的操作:兩次call()函數(shù)調用觸發(fā)兩個獨立副作用,兩次subscribe()訂閱觸發(fā)兩個獨立的副作用。相反的,EventEmitters(事件驅動)共享副作用并且無論是否存在訂閱者都會盡早執(zhí)行,Observables 與之相反,不會共享副作用并且是延遲執(zhí)行。

訂閱一個可觀察對象類似于調用一個函數(shù)。

一些人認為可觀察對象是異步的。這并不確切,如果你用一些log語句包圍在訂閱程序的前后:

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

輸出如下:

"before"
"Hello"
42
"after"

以上可以顯示對foo的訂閱是完全同步的,就像調用一個函數(shù)。

可觀察對象以同步或者異步的方式發(fā)送多個值。

那它和普通函數(shù)有哪些不同之處呢?

可觀察對象可以隨時間"return"多個值。然而函數(shù)卻做不到,你不能夠使得如下的情況發(fā)生:

function foo() {
    console.log('Hello');
    return 42;
    return 100; // dead code. will never happen
}

函數(shù)僅僅可以返回一個值,然而,不要驚訝,可觀察對象卻可以做到這些:

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); // happens asynchronously
    }, 1000);
});

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

同步輸出:

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

總結:

  1. fun.call()意味著"同步地給我一個值"
  2. observable.subscribe()意味著"給我任意多個值,同步也好異步也罷。"

Observable 剖析

Observables(可觀察對象) 是使用 Rx.Observable.create 或創(chuàng)建操作符創(chuàng)建的,并使用觀察者來訂閱它,然后執(zhí)行它并發(fā)送 next / error / complete 通知給觀察者,而且執(zhí)行可能會被清理。這四個方面全部編碼進 Observables 實例中,但某些方面是與其他類型相關的,像 Observer (觀察者) 和 Subscription (訂閱)。

Observable 的核心關注點:

-創(chuàng)建 Observables(可觀察對象)
-訂閱 Observables(可觀察對象)
-執(zhí)行 Observables(可觀察對象)
-清理 Observables(可觀察對象)

創(chuàng)建 Observables(可觀察對象)

Rx.Observable.createObservable 構造函數(shù)的別名,它接收一個參數(shù):subscribe函數(shù)。

下面的示例創(chuàng)建了一個 Observable(可觀察對象),它每隔一秒會向觀察者發(fā)送字符串 'hi' 。

var observable = Rx.Observable.create(function subscribe(observer) { // 通常我們會像之前的案例一樣,省略subscribe這個名字
  var id = setInterval(() => {
    observer.next('hi')
  }, 1000);
});

Observables 可以使用 create 來創(chuàng)建, 但通常我們使用所謂的創(chuàng)建操作符, 像 of、from、interval、等等。

在上面的示例中,subscribe 函數(shù)是用來描述 Observable 最重要的一塊。我們來看下訂閱是什么意思。

訂閱 Observables

示例中的 Observable 對象創(chuàng)建的 observable 可以訂閱,像這樣:

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

observable.subscribeObservable.create(function subscribe(observer) {...})中的 subscribe有著同樣的名字,這并不是一個巧合。在Rx庫中,它們是不同的。但從實際出發(fā),你可以認為在概念上它們是等同的。

subscribe 調用在同一 Observable(可觀察對象) 的多個觀察者之間是不共享的。當使用一個觀察者調用 observable.subscribe 時,Observable.create(function subscribe(observer) {...}) 中的 subscribe 函數(shù)只服務于給定的觀察者。對 observable.subscribe 的每次調用都會觸發(fā)針對給定觀察者的獨立設置。

訂閱 Observable 像是調用函數(shù), 并提供接收數(shù)據(jù)的回調函數(shù)。

這與像 addEventListener / removeEventListener 這樣的事件處理方法 API 是完全不同的。使用 observable.subscribe,在 Observable 中不會將給定的觀察者注冊為監(jiān)聽器。Observable 甚至不會去維護一個附加的觀察者列表。

subscribe 調用是啟動 “Observable 執(zhí)行”的一種簡單方式, 并將值或事件傳遞給本次執(zhí)行的觀察者。

整體性案例:

程序先同步執(zhí)行,過1s之后執(zhí)行異步,之后點擊按鈕執(zhí)行事件。

var observable = Rx.Observable.create(function subscribe (observer) { // 創(chuàng)建Observable
    console.log('start-----------')
    observer.next(42) // 同步執(zhí)行
    observer.next(100)
    observer.next(200)
    setTimeout(() => { // 異步執(zhí)行
        observer.next(300)
    }, 1000)
    var button = document.getElementById('rx-eventListener')
    button.addEventListener('click', () => { // 不知何時執(zhí)行
        console.log('Clicked!')
        observer.next('Clicked-end')
    })
    console.log('end-------------')
})
observable.subscribe(x => { // 訂閱Observable
    console.log('觀察者1')
    console.log(x)
})
observable.subscribe(x => { // 訂閱Observable
    console.log('觀察者2')
    console.log(x)
})

結果如下:

案例-觀察者間相互獨立-整體性案例

這里我們可以看到觀察者1觀察者2雖然從發(fā)布者那里拿到的值是一樣的,但是每個觀察者都是相互獨立的。

執(zhí)行 Observables

Observable.create(function subscribe(observer) {...})...的代碼表示 “Observable 執(zhí)行”,它是惰性運算,只有在每個觀察者訂閱后才會執(zhí)行。隨著時間的推移,執(zhí)行會以同步或異步的方式產(chǎn)生多個值。

Observable 執(zhí)行可以傳遞三種類型的值:

-"Next" 通知: 發(fā)送一個值,比如數(shù)字、字符串、對象,等等。
-"Error" 通知: 發(fā)送一個 JavaScript 錯誤 或 異常。
-"Complete" 通知: 不再發(fā)送任何值。

"Next" 通知是最重要,也是最常見的類型:它們表示傳遞給觀察者的實際數(shù)據(jù)。"Error" 和 "Complete" 通知可能只會在 Observable 執(zhí)行期間發(fā)生一次,并且只會執(zhí)行其中的一個。

這些約束用所謂的 Observable 語法或合約表達最好,寫為正則表達式是這樣的:

next*(error|complete)?

在 Observable 執(zhí)行中, 可能會發(fā)送零個到無窮多個 "Next" 通知。如果發(fā)送的是 "Error" 或 "Complete" 通知的話,那么之后不會再發(fā)送任何通知了。

下面是 Observable 執(zhí)行的示例,它發(fā)送了三個 "Next" 通知,然后是 "Complete" 通知:

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

Observable 嚴格遵守自身的規(guī)約,所以下面的代碼不會發(fā)送 "Next" 通知 4:

var observable = Rx.Observable.create(function subscribe(observer) {
  observer.next(1);
  observer.next(2);
  observer.next(3);
  observer.complete();
  observer.next(4); // 因為違反規(guī)約,所以不會發(fā)送
});

在 subscribe 中用 try/catch 代碼塊來包裹任意代碼是個不錯的主意,如果捕獲到異常的話,會發(fā)送 "Error" 通知:

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); // 如果捕獲到異常會發(fā)送一個錯誤
  }
});

整體性案例:

var observable = Rx.Observable.create(function subscribe (observer) {
    try {
        observer.next(1)
        observer.next(2)
        observer.next(3)
        observer.complete() // 不再發(fā)送任何值
        observer.next(4) // 因為違反規(guī)約,所以不會發(fā)送
    } catch (err) {
        observer.error(err) // 如果捕獲到異常會發(fā)送一個 JavaScript 錯誤 或 異常
    }
})
observable.subscribe(x => { // 正常
    console.log('觀察者-正常')
    console.log(x)
})
observable.subscribe(x => { // 模擬異常
    throw new Error('拋出一個異常')
    console.log('觀察者-異常')
    console.log(x)
})

清理 Observable 執(zhí)行

因為 Observable 執(zhí)行可能會是無限的,并且觀察者通常希望能在有限的時間內中止執(zhí)行,所以我們需要一個 API 來取消執(zhí)行。因為每個執(zhí)行都是其對應觀察者專屬的,一旦觀察者完成接收值,它必須要一種方法來停止執(zhí)行,以避免浪費計算能力或內存資源。(每一個觀察者都是互相獨立的)

當調用了 observable.subscribe ,觀察者會被附加到新創(chuàng)建的Observable 執(zhí)行中。這個調用還返回一個對象,即 Subscription (訂閱):

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

Subscription 表示進行中的執(zhí)行,它有最小化的 API 以允許你取消執(zhí)行。使用 subscription.unsubscribe()你可以取消進行中的執(zhí)行:

var observable = Rx.Observable.from([10, 20, 30])
var subscription = observable.subscribe(x => console.log(x)) // 10 20 30
subscription.unsubscribe() // 同步執(zhí)行完成立馬清除

當你訂閱了 Observable,你會得到一個 Subscription ,它表示進行中的執(zhí)行。只要調用 unsubscribe() 方法就可以取消執(zhí)行。

當我們使用 create() 方法創(chuàng)建 Observable 時,Observable 必須定義如何清理執(zhí)行的資源。你可以通過在 function subscribe() 中返回一個自定義的 unsubscribe 函數(shù)。

舉例來說,這是我們如何清理使用了 setInterval 的 interval 執(zhí)行集合:

var cont = 0
var setInterObs = Rx.Observable.create(function subscribe(observer) {
    // 追蹤 interval 資源
    var intervalID = setInterval(() => {
        cont++
        observer.next('hi')
    }, 1000)

    // 提供取消和清理 interval 資源的方法
    return function unsubscribe() {
        cont = 0
        clearInterval(intervalID)
    }
})
var unsubscribe = setInterObs.subscribe(function (x) { // 如果不使用箭頭函數(shù),回調中的 this 代表 subscription
    if (cont < 10) {
        console.log(x)
    } else {
        console.log(this)
        console.log('清除')
        this.unsubscribe()
        console.log(cont) // 我們可以發(fā)現(xiàn)cont被重置為0,這表明從 subscribe 返回的 unsubscribe 在概念上也等同于 subscription.unsubscribe。
    }
})

執(zhí)行結果如圖:

案例-清理 Observable 執(zhí)行

我們可以從執(zhí)行結果里看到 subscription 的unsubscribe方法作為 subscription 的私有方法 _unsubscribe

并且從 console.log(cont) 等于0,我們可以知道正如 observable.subscribe類似于 Observable.create(function subscribe() {...}),從 subscribe返回的 unsubscribe 在概念上也等同于 subscription.unsubscribe。(即我們在執(zhí)行subscription.unsubscribe時,從 subscribe返回的 unsubscribe 也是會被執(zhí)行的)

事實上,如果我們拋開圍繞這些概念的 ReactiveX 類型,也就只剩下更加直觀的JavaScript。代碼如下:

function subscribe (observer) {
    var intervalID = setInterval(() => {
        observer.next('hello')
    }, 1000)

    return function unsubscribe() {
        clearInterval(intervalID)
    }
}
var unsubscribe = subscribe({next: (x) => console.log(x)})
unsubscribe() // 清理資源

為什么我們要使用像 Observable、Observer 和 Subscription 這樣的 Rx 類型?原因是保證代碼的安全性(比如 Observable 規(guī)約)和操作符的可組合性。

結語

這里我們大致已經(jīng)了解了Rxjs的可觀察對象(Observables)。

再回顧一下前面的內容:

觀察者模式又叫發(fā)布-訂閱(Publish/Subscribe)模式

他定義了一種一對多的依賴關系,讓多個觀察者對象同時監(jiān)聽某一個主題對象。這個主題對象在狀態(tài)發(fā)生變化時,會通知所有觀察者對象,使他們能夠自動更新自己。而且各個觀察者之間相互獨立。

這樣的好處是:

  1. 我們只需要針對可觀察對象這一抽象的主題對象接口編程,減少了與具體的耦合,即他只是一個抽象的通知者。
  2. 觀察者只依賴主題對象的狀態(tài),這意味著維持各個觀察者的一致性,但又保證了各個觀察者是相互獨立的。
producer(生產(chǎn)者) consumer (消費者) 單個值 多個值
pull拉 Passive(被動的一方):被請求的時候產(chǎn)生數(shù)據(jù) Active(起主導的一方):決定何時請求數(shù)據(jù) Function Iterator
push推 Active:按自己的節(jié)奏生產(chǎn)數(shù)據(jù) Passive:對接收的數(shù)據(jù)做出反應(處理接收到的數(shù)據(jù)) Promise Observable

可觀察對象(Observables)像是沒有參數(shù), 但可以泛化為允許返回多個值的函數(shù)。訂閱一個可觀察對象類似于調用一個函數(shù)??捎^察對象以同步或者異步的方式發(fā)送多個值。

使用:

創(chuàng)建:Rx.Observable.create(function subscribe(observer) {...})。Observables 可以使用 create 來創(chuàng)建, 但通常我們使用所謂的創(chuàng)建操作符, 像 of、from、interval、等等。如:Rx.Observable.from([10, 20, 30])
訂閱:observable.subscribe(x => {...})
執(zhí)行:傳遞值的話,會發(fā)送"Next" 通知observer.next(value);結束的話,會發(fā)送 "Complete" 通知observer.complete();捕獲到異常的話,會發(fā)送 "Error" 通知observer.error(err)。在 Observable 執(zhí)行中, 可能會發(fā)送零個到無窮多個 "Next" 通知。如果發(fā)送的是 "Error" 或 "Complete" 通知的話,那么之后不會再發(fā)送任何通知了。
清理:subscription.unsubscribe()。當你訂閱了 Observable,你會得到一個 Subscription ,它表示進行中的執(zhí)行。只要調用 unsubscribe()方法就可以取消執(zhí)行。

提示:后面還有精彩敬請期待,請大家關注我的專題:web前端。如有意見可以進行評論,每一條評論我都會認真對待。

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發(fā)布,文章內容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 230,247評論 6 543
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 99,520評論 3 429
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事?!?“怎么了?”我有些...
    開封第一講書人閱讀 178,362評論 0 383
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經(jīng)常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,805評論 1 317
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 72,541評論 6 412
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 55,896評論 1 328
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,887評論 3 447
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 43,062評論 0 290
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 49,608評論 1 336
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 41,356評論 3 358
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 43,555評論 1 374
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 39,077評論 5 364
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 44,769評論 3 349
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 35,175評論 0 28
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 36,489評論 1 295
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 52,289評論 3 400
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 48,516評論 2 379

推薦閱讀更多精彩內容

  • 作者: maplejaw本篇只解析標準包中的操作符。對于擴展包,由于使用率較低,如有需求,請讀者自行查閱文檔。 創(chuàng)...
    maplejaw_閱讀 45,759評論 8 93
  • 本篇文章介主要紹RxJava中操作符是以函數(shù)作為基本單位,與響應式編程作為結合使用的,對什么是操作、操作符都有哪些...
    嘎啦果安卓獸閱讀 2,887評論 0 10
  • 注:只包含標準包中的操作符,用于個人學習及備忘參考博客:http://blog.csdn.net/maplejaw...
    小白要超神閱讀 2,213評論 2 8
  • 響應式編程簡介 響應式編程是一種基于異步數(shù)據(jù)流概念的編程模式。數(shù)據(jù)流就像一條河:它可以被觀測,被過濾,被操作,或者...
    說碼解字閱讀 3,092評論 0 5
  • 參考:給 Android 開發(fā)者的 RxJava 詳解-扔物線深入淺出RxJava 基礎 "a library f...
    Vincen1024閱讀 546評論 0 1