RxJS系列教程(九) 操作異步流

Rx,不管你是JS砸民,Java抵怎,Python還是Swift,玩的就是操作符岭参。每個操作符怎么用反惕,官方文檔寫得不能再清楚了,再配上例子和圖演侯,您要還整不明白就……繼續(xù)整姿染,整到明白為止,量變肯定會導(dǎo)致質(zhì)變秒际,就像國足永遠(yuǎn)不爭氣悬赏,都是永恒的真理。

然而整明白了操作符是干啥的程癌,確不知道在啥地方用舷嗡,這就是cookbook或者recipe之類書籍有市場的原因。說白了就像中國的教育嵌莉,理論都是大拿进萄,實踐都是newbie。

當(dāng)然锐峭,概念還是要講的中鼠,理論是基礎(chǔ),實踐是真理沿癞。下面我們來看一個場景:一個web app援雇,電腦上用,用鼠標(biāo)椎扬,手機(jī)平板上用惫搏,用手指具温,這不廢話么!然而懂行的人知道里面有奧妙筐赔,用鼠標(biāo)的铣猩,會觸發(fā)mousedown,mouseup茴丰,mousemove事件达皿,用手指的,會觸發(fā)touchstart贿肩,touchend峦椰,touchmove事件。拋出來的問題是汰规,能不能只寫一套邏輯供兩套事件使用呢汤功?當(dāng)然能了,拷貝粘貼唄控轿。我們來看看RxJS中怎么做冤竹。

const mouseupStream = Rx.Observable.fromEvent(document, 'mouseup');// 鼠標(biāo)按鍵抬起事件流
const touchendStream = Rx.Observable.fromEvent(document, 'touchend');// 觸摸屏幕抬起事件流

mouseupStream.subscribe(/*處理邏輯*/);
touchedStream.subscribe(/*處理邏輯*/);

這不和沒用RxJS一個意思么。別著急啊茬射,沒說這是最佳解決方案啊鹦蠕,接著往下看。

這兩個事件是非常類似的在抛,既然類似钟病,我們能不能把它們當(dāng)成一樣的事件,合并起來呢刚梭?答案是肠阱,必須可以!

RxJS中的merge()操作符就是用來合并兩個流的朴读,既然是兩個屹徘,就會有順序問題,如果是同步操作衅金,那就是有序的噪伊;如果是異步操作,merge()操作符內(nèi)部會根據(jù)時間來做決定氮唯,合并起來的流中的事件就是無序的鉴吹,交叉出現(xiàn)的。這里說句題外話惩琉,其實將操作符都要配圖的豆励,但是我實在是懶,況且圖也不是我畫的瞒渠,大家就自己上網(wǎng)站上看吧良蒸。

merge()既有靜態(tài)方法實現(xiàn)也有實例方法實現(xiàn)技扼,以后講操作符不額外聲明的話,默認(rèn)都有兩種實現(xiàn)嫩痰。

Rx.Observable.merge(mouseupStream, touchendStream);
//或
mouseupStream.merge(touchendStream);

假如我們最終的需求是要鼠標(biāo)點擊或手指觸摸位置的數(shù)據(jù)淮摔,請看代碼:

Rx.Observable.merge(mouseupStream, touchendStream)
  .do(event => console.log(event.type))// debug,查看事件類型
  .map(event => {
    switch (event.type) {
      case 'touchend':
        return {
          left: event.changedTouches[0].clientX,
          top: event.changedTouches[0].clientY
        };
      case 'mouseup':
        return {
          left: event.clientX,
          top: event.clientY
        }
    }
  })
  .subscribe(object => {
    console.log(`位置坐標(biāo)為:(${object.left}, ${object.top})`);
  })

我們知道Rx一脈相承自函數(shù)式編程始赎,那這段代碼就有點說不過去了,怎能出現(xiàn)命令式的控制語句呢仔燕,說的就是你switch造垛!當(dāng)然理想化的東西能不能實現(xiàn)還是一回事兒呢,況且規(guī)則是人定的晰搀。但我們盡量在操作符中不出現(xiàn)命令式語句五辽,把不得不出現(xiàn)的邏輯推遲到observer端來處理。我在系列三中提到過副作用也都放到observer來處理外恕。

當(dāng)然更好的方式是杆逗,我們在observable端就把數(shù)據(jù)處理好,observer接收時就不用再做處理了鳞疲。

const pmouseupStream = mouseupStream.map(event => ({
  left: event.clientX,
  top: event.clientY
}));

const ptouchendStream = touchendStream.map(event => ({
  left: event.changedTouches[0].clientX,
  top: event.changedTouches[0].clientY
}));

Rx.Observable.merge(pmouseupStream, ptouchendStream)
  .subscribe(object => {
    console.log(`位置坐標(biāo)為:(${object.left}, ${object.top})`);
  });

完美罪郊!

沒有任何事會一直完美下去的。如果我們想要兩個異步流合并后保持先后順序呢尚洽?沒問題悔橄,concat()操作符完美解決你的問題。這個沒啥可講的腺毫,同步流能用嗎癣疟?您自己試試吧,實踐才是真理潮酒。

繼續(xù)繼續(xù)睛挚。剛才命令式的switch被我們痛罵了一頓,RxJS中的switch()可就是個寶兒嘍急黎。叫李嘉誠的千千萬扎狱,然而首富就那么一個,這就是差距叁熔。

switch()操作符只有實例方法實現(xiàn)方式委乌。它的作用是切換到最新的那個observable。這到底是啥意思呢荣回,請看圖:

switch操作符
switch操作符

當(dāng)我們第一次點擊按鈕的時候遭贸,map中的函數(shù)返回值又是一個observable,也就是說心软,switch接收到的是一個內(nèi)嵌observable的observable壕吹,這時候switch會用內(nèi)嵌的observable取代外層的那個observable著蛙,也就是click事件流被新產(chǎn)生的時間事件流取代了。

當(dāng)我們在第二次點擊按鈕的時候(這個時候上個時間事件流還沒有結(jié)束)耳贬,又會產(chǎn)生一個新的時間事件流踏堡,這個新的時間事件流不僅取代了click事件流,還取代了第一次點擊按鈕產(chǎn)生的那個時間事件流咒劲。謹(jǐn)記switch永遠(yuǎn)會切換到最新的那個事件流顷蟆。

內(nèi)嵌observable引出了merge(),concat()腐魂,switch()各自的兄弟:mergeMap()帐偎,concatMap(),switchMap()蛔屹。

mergeMap

還用上面的例子削樊,把map改成mergeMap,去掉switch兔毒,區(qū)別是漫贞,click事件流同樣被取代,但第一次點擊產(chǎn)生的時間事件流不會被第二次點擊的時間事件流取代育叁,而是合并成了一個流(無序)迅脐。

switchMap

這個操作符完成的事兒實際就是上面例子中的map+switch。

concatMap

用concatMap替換map豪嗽,去掉switch仪际,點擊三次按鈕,我們會看到控制臺輸出三次0到4昵骤,前一次不結(jié)束树碱,后面的一直等待。這里給大家一個贊賞我的機(jī)會变秦,請用三個鼠標(biāo)事件流+concatMap操作符+takeUntil操作符完成拖放頁面元素的功能成榜。你會發(fā)現(xiàn),哇~好簡單好明了蹦玫。

takeUntil操作符接收一個observable為參數(shù)赎婚,含義是,接收上游事件并讓它通過樱溉,直到參數(shù)observable開始發(fā)送事件挣输。

其實更直觀的感受這些操作符的強(qiáng)大之處,或者說Rx的強(qiáng)大之處福贞,應(yīng)該用ajax撩嚼、promise這些更貼近日常開發(fā)的例子,譬如說之前提到過的搜索框提示,或者監(jiān)控股票價格完丽,氣象溫度等等恋技,就留個各位自己嘗試吧,實踐出真知嘛逻族。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末蜻底,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子聘鳞,更是在濱河造成了極大的恐慌薄辅,老刑警劉巖,帶你破解...
    沈念sama閱讀 206,602評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件抠璃,死亡現(xiàn)場離奇詭異长搀,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)鸡典,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,442評論 2 382
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來枪芒,“玉大人彻况,你說我怎么就攤上這事【俗伲” “怎么了纽甘?”我有些...
    開封第一講書人閱讀 152,878評論 0 344
  • 文/不壞的土叔 我叫張陵,是天一觀的道長抽碌。 經(jīng)常有香客問我悍赢,道長,這世上最難降的妖魔是什么货徙? 我笑而不...
    開封第一講書人閱讀 55,306評論 1 279
  • 正文 為了忘掉前任左权,我火速辦了婚禮,結(jié)果婚禮上痴颊,老公的妹妹穿的比我還像新娘赏迟。我一直安慰自己,他們只是感情好蠢棱,可當(dāng)我...
    茶點故事閱讀 64,330評論 5 373
  • 文/花漫 我一把揭開白布锌杀。 她就那樣靜靜地躺著,像睡著了一般泻仙。 火紅的嫁衣襯著肌膚如雪糕再。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,071評論 1 285
  • 那天玉转,我揣著相機(jī)與錄音突想,去河邊找鬼。 笑死,一個胖子當(dāng)著我的面吹牛蒿柳,可吹牛的內(nèi)容都是我干的饶套。 我是一名探鬼主播,決...
    沈念sama閱讀 38,382評論 3 400
  • 文/蒼蘭香墨 我猛地睜開眼垒探,長吁一口氣:“原來是場噩夢啊……” “哼妓蛮!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起圾叼,我...
    開封第一講書人閱讀 37,006評論 0 259
  • 序言:老撾萬榮一對情侶失蹤蛤克,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后夷蚊,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體构挤,經(jīng)...
    沈念sama閱讀 43,512評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 35,965評論 2 325
  • 正文 我和宋清朗相戀三年惕鼓,在試婚紗的時候發(fā)現(xiàn)自己被綠了筋现。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 38,094評論 1 333
  • 序言:一個原本活蹦亂跳的男人離奇死亡箱歧,死狀恐怖矾飞,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情呀邢,我是刑警寧澤洒沦,帶...
    沈念sama閱讀 33,732評論 4 323
  • 正文 年R本政府宣布,位于F島的核電站价淌,受9級特大地震影響申眼,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜蝉衣,卻給世界環(huán)境...
    茶點故事閱讀 39,283評論 3 307
  • 文/蒙蒙 一括尸、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧病毡,春花似錦姻氨、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,286評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至功戚,卻和暖如春娶眷,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背啸臀。 一陣腳步聲響...
    開封第一講書人閱讀 31,512評論 1 262
  • 我被黑心中介騙來泰國打工届宠, 沒想到剛下飛機(jī)就差點兒被人妖公主榨干…… 1. 我叫王不留烁落,地道東北人。 一個月前我還...
    沈念sama閱讀 45,536評論 2 354
  • 正文 我出身青樓豌注,卻偏偏與公主長得像伤塌,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子轧铁,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 42,828評論 2 345

推薦閱讀更多精彩內(nèi)容