30 天精通 RxJS (08):簡(jiǎn)易拖拉實(shí)作 - take, first, takeUntil, concatAll

我們今天要接著講 take, first, takeUntil, concatAll 這四個(gè) operators硅则,并且實(shí)作一個(gè)簡(jiǎn)易的拖拉功能厚者。

Operators

take

take 是一個(gè)很簡(jiǎn)單的 operator因宇,顧名思義就是取前幾個(gè)元素后就結(jié)束葛作,范例如下

var source = Rx.Observable.interval(1000);
var example = source.take(3);

example.subscribe({
    next: (value) => { console.log(value); },
    error: (err) => { console.log('Error: ' + err); },
    complete: () => { console.log('complete'); }
});
// 0
// 1
// 2
// complete

JSBin | JSFiddle

這裡可以看到我們的 source 原本是會(huì)發(fā)出無(wú)限個(gè)元素的箭窜,但這裡我們用 take(3) 就會(huì)只取前 3 個(gè)元素膀篮,取完后就直接結(jié)束(complete)嘹狞。

用 Marble diagram 表示如下

source : -----0-----1-----2-----3--..
                take(3)
example: -----0-----1-----2|

first

first 會(huì)取 observable 送出的第 1 個(gè)元素之后就直接結(jié)束,行為跟 take(1) 一致誓竿。

var source = Rx.Observable.interval(1000);
var example = source.first();

example.subscribe({
    next: (value) => { console.log(value); },
    error: (err) => { console.log('Error: ' + err); },
    complete: () => { console.log('complete'); }
});

// 0
// complete

JSBin | JSFiddle

用 Marble diagram 表示如下

source : -----0-----1-----2-----3--..
                first()
example: -----0|

takeUntil

在實(shí)務(wù)上 takeUntil 很常使用到磅网,他可以在某件事情發(fā)生時(shí),讓一個(gè) observable 直送出 完成(complete)訊息筷屡,范例如下

var source = Rx.Observable.interval(1000);
var click = Rx.Observable.fromEvent(document.body, 'click');
var example = source.takeUntil(click);     

example.subscribe({
    next: (value) => { console.log(value); },
    error: (err) => { console.log('Error: ' + err); },
    complete: () => { console.log('complete'); }
});
// 0
// 1
// 2
// 3
// complete (點(diǎn)擊body了

JSBin | JSFiddle

這裡我們一開(kāi)始先用 interval 建立一個(gè) observable涧偷,這個(gè) observable 每隔 1 秒會(huì)送出一個(gè)從 0 開(kāi)始遞增的數(shù)值,接著我們用 takeUntil速蕊,傳入另一個(gè) observable嫂丙。

當(dāng) takeUntil 傳入的 observable 發(fā)送值時(shí),原本的 observable 就會(huì)直接進(jìn)入完成(complete)的狀態(tài)规哲,并且發(fā)送完成訊息跟啤。也就是說(shuō)上面這段程式碼的行為,會(huì)先每 1 秒印出一個(gè)數(shù)字(從 0 遞增)直到我們點(diǎn)擊 body 為止,他才會(huì)送出 complete 訊息隅肥。

如果畫(huà)成 Marble Diagram 則會(huì)像下面這樣

source : -----0-----1-----2------3--
click  : ----------------------c----
                takeUntil(click)
example: -----0-----1-----2----|

當(dāng) click 一發(fā)送元素的時(shí)候竿奏,observable 就會(huì)直接完成(complete)。

concatAll

有時(shí)我們的 Observable 送出的元素又是一個(gè) observable腥放,就像是二維陣列泛啸,陣列裡面的元素是陣列,這時(shí)我們就可以用 concatAll 把它攤平成一維陣列秃症,大家也可以直接把 concatAll 想成把所有元素 concat 起來(lái)候址。

var click = Rx.Observable.fromEvent(document.body, 'click');
var source = click.map(e => Rx.Observable.of(1,2,3));

var example = source.concatAll();
example.subscribe({
    next: (value) => { console.log(value); },
    error: (err) => { console.log('Error: ' + err); },
    complete: () => { console.log('complete'); }
});

JSBin | JSFiddle

這個(gè)范例我們每點(diǎn)擊一次 body 就會(huì)立刻送出 1,2,3,如果用 Marble diagram 表示則如下

click  : ------c------------c--------

        map(e => Rx.Observable.of(1,2,3))

source : ------o------------o--------
                \            \
                 (123)|       (123)|

                   concatAll()

example: ------(123)--------(123)------------

這裡可以看到 source observable 內(nèi)部每次發(fā)送的值也是 observable种柑,這時(shí)我們用 concatAll 就可以把 source 攤平成 example岗仑。

這裡需要注意的是 concatAll 會(huì)處理 source 先發(fā)出來(lái)的 observable,必須等到這個(gè) observable 結(jié)束聚请,才會(huì)再處理下一個(gè) source 發(fā)出來(lái)的 observable荠雕,讓我們用下面這個(gè)范例說(shuō)明。

var obs1 = Rx.Observable.interval(1000).take(5);
var obs2 = Rx.Observable.interval(500).take(2);
var obs3 = Rx.Observable.interval(2000).take(1);

var source = Rx.Observable.of(obs1, obs2, obs3);

var example = source.concatAll();

example.subscribe({
    next: (value) => { console.log(value); },
    error: (err) => { console.log('Error: ' + err); },
    complete: () => { console.log('complete'); }
});
// 0
// 1
// 2
// 3
// 4
// 0
// 1
// 0
// complete

JSBin | JSFiddle

這裡可以看到 source 會(huì)送出 3 個(gè) observable驶赏,但是 concatAll 后的行為永遠(yuǎn)都是先處理第一個(gè) observable炸卑,等到當(dāng)前處理的結(jié)束后才會(huì)再處理下一個(gè)

用 Marble diagram 表示如下

source : (o1                 o2      o3)|
           \                  \       \
            --0--1--2--3--4|   -0-1|   ----0|

                concatAll()        

example: --0--1--2--3--4-0-1----0|

簡(jiǎn)易拖拉

當(dāng)學(xué)完前面幾個(gè) operator 后煤傍,我們就很輕鬆地做出拖拉的功能盖文,先讓我們來(lái)看一下需求

  1. 首先畫(huà)面上有一個(gè)元件(#drag)
  2. 當(dāng)滑鼠在元件(#drag)上按下左鍵(mousedown)時(shí),開(kāi)始監(jiān)聽(tīng)滑鼠移動(dòng)(mousemove)的位置
  3. 當(dāng)滑鼠左鍵放掉(mouseup)時(shí)患久,結(jié)束監(jiān)聽(tīng)滑鼠移動(dòng)
  4. 當(dāng)滑鼠移動(dòng)(mousemove)被監(jiān)聽(tīng)時(shí)椅寺,跟著修改元件的樣式屬性

第一步我已經(jīng)完成了,大家可以直接到以下兩個(gè)連結(jié)做練習(xí)

第二步我們要先取得各個(gè) DOM 物件蒋失,元件(#drag) 跟 body。

const dragDOM = document.getElementById('drag');
const body = document.body;

要取得 body 的原因是因?yàn)榛笠苿?dòng)(mousemove)跟滑鼠左鍵放掉(mouseup)都應(yīng)該是在整個(gè) body 監(jiān)聽(tīng)桐玻。

第三步我們寫(xiě)出各個(gè)會(huì)用到的監(jiān)聽(tīng)事件篙挽,并用 fromEvent 來(lái)取得各個(gè) observable。

  • 對(duì) #drag 監(jiān)聽(tīng) mousedown
  • 對(duì) body 監(jiān)聽(tīng) mouseup
  • 對(duì) body 監(jiān)聽(tīng) mousemove
const mouseDown = Rx.Observable.fromEvent(dragDOM, 'mousedown');
const mouseUp = Rx.Observable.fromEvent(body, 'mouseup');
const mouseMove = Rx.Observable.fromEvent(body, 'mousemove');

記得還沒(méi) subscribe 之前都不會(huì)開(kāi)始監(jiān)聽(tīng)镊靴,一定會(huì)等到 subscribe 之后 observable 才會(huì)開(kāi)始送值铣卡。

第四步開(kāi)始寫(xiě)邏輯

當(dāng) mouseDown 時(shí),轉(zhuǎn)成 mouseMove 的事件

const source = mouseDown.map(event => mouseMove)

mouseMove 要在 mouseUp 后結(jié)束

加上 takeUntil(mouseUp)

const source = mouseDown
               .map(event => mouseMove.takeUntil(mouseUp))

這時(shí) source 大概長(zhǎng)像這樣

source: -------e--------------e-----
                \              \
                  --m-m-m-m|     -m--m-m--m-m|

m 代表 mousemove event

concatAll() 攤平 source 成一維偏竟。

const source = mouseDown
               .map(event => mouseMove.takeUntil(mouseUp))
               .concatAll();                 

用 map 把 mousemove event 轉(zhuǎn)成 x,y 的位置煮落,并且訂閱。

source
.map(m => {
    return {
        x: m.clientX,
        y: m.clientY
    }
})
.subscribe(pos => {
    dragDOM.style.left = pos.x + 'px';
    dragDOM.style.top = pos.y + 'px';
})              

到這裡我們就已經(jīng)完成了簡(jiǎn)易的拖拉功能了!完整的程式碼如下

const dragDOM = document.getElementById('drag');
const body = document.body;

const mouseDown = Rx.Observable.fromEvent(dragDOM, 'mousedown');
const mouseUp = Rx.Observable.fromEvent(body, 'mouseup');
const mouseMove = Rx.Observable.fromEvent(body, 'mousemove');

mouseDown
  .map(event => mouseMove.takeUntil(mouseUp))
  .concatAll()
  .map(event => ({ x: event.clientX, y: event.clientY }))
  .subscribe(pos => {
    dragDOM.style.left = pos.x + 'px';
    dragDOM.style.top = pos.y + 'px';
  })

不知道讀者有沒(méi)有感受到踊谋,我們整個(gè)程式碼不到 15 行蝉仇,而且只要能夠看懂各個(gè) operators,我們程式可讀性是非常的高。

雖然這只是一個(gè)簡(jiǎn)單的拖拉實(shí)現(xiàn)轿衔,但已經(jīng)展示出 RxJS 帶來(lái)的威力沉迹,它讓我們的程式碼更加的簡(jiǎn)潔,也更好的維護(hù)害驹!

這裡有完整的成果可以參考鞭呕。

今日小結(jié)

我們今天介紹了四個(gè) operators 分別是 take, first, takeUntil, concatAll,并且完成了一個(gè)簡(jiǎn)易的拖拉功能宛官,我們之后會(huì)把這個(gè)拖拉功能做得更完整葫松,并且整合其他功能!

不知道讀者今天有沒(méi)有收穫底洗?如果有任何問(wèn)題进宝,歡迎在下方留言給我!
如果你喜歡這篇文章枷恕,請(qǐng)至標(biāo)題旁幫我按個(gè) 星星+like党晋,謝謝。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末徐块,一起剝皮案震驚了整個(gè)濱河市未玻,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌胡控,老刑警劉巖扳剿,帶你破解...
    沈念sama閱讀 222,681評(píng)論 6 517
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異昼激,居然都是意外死亡庇绽,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 95,205評(píng)論 3 399
  • 文/潘曉璐 我一進(jìn)店門(mén)橙困,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)瞧掺,“玉大人,你說(shuō)我怎么就攤上這事凡傅”俦罚” “怎么了?”我有些...
    開(kāi)封第一講書(shū)人閱讀 169,421評(píng)論 0 362
  • 文/不壞的土叔 我叫張陵夏跷,是天一觀的道長(zhǎng)哼转。 經(jīng)常有香客問(wèn)我,道長(zhǎng)槽华,這世上最難降的妖魔是什么壹蔓? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 60,114評(píng)論 1 300
  • 正文 為了忘掉前任,我火速辦了婚禮猫态,結(jié)果婚禮上佣蓉,老公的妹妹穿的比我還像新娘披摄。我一直安慰自己,他們只是感情好偏螺,可當(dāng)我...
    茶點(diǎn)故事閱讀 69,116評(píng)論 6 398
  • 文/花漫 我一把揭開(kāi)白布行疏。 她就那樣靜靜地躺著,像睡著了一般套像。 火紅的嫁衣襯著肌膚如雪酿联。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書(shū)人閱讀 52,713評(píng)論 1 312
  • 那天夺巩,我揣著相機(jī)與錄音贞让,去河邊找鬼。 笑死柳譬,一個(gè)胖子當(dāng)著我的面吹牛喳张,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播美澳,決...
    沈念sama閱讀 41,170評(píng)論 3 422
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼销部,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了制跟?” 一聲冷哼從身側(cè)響起舅桩,我...
    開(kāi)封第一講書(shū)人閱讀 40,116評(píng)論 0 277
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎雨膨,沒(méi)想到半個(gè)月后擂涛,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 46,651評(píng)論 1 320
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡聊记,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,714評(píng)論 3 342
  • 正文 我和宋清朗相戀三年撒妈,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片排监。...
    茶點(diǎn)故事閱讀 40,865評(píng)論 1 353
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡狰右,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出社露,到底是詐尸還是另有隱情挟阻,我是刑警寧澤,帶...
    沈念sama閱讀 36,527評(píng)論 5 351
  • 正文 年R本政府宣布峭弟,位于F島的核電站,受9級(jí)特大地震影響脱拼,放射性物質(zhì)發(fā)生泄漏瞒瘸。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 42,211評(píng)論 3 336
  • 文/蒙蒙 一熄浓、第九天 我趴在偏房一處隱蔽的房頂上張望情臭。 院中可真熱鬧省撑,春花似錦、人聲如沸俯在。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 32,699評(píng)論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)跷乐。三九已至肥败,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間愕提,已是汗流浹背馒稍。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 33,814評(píng)論 1 274
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留浅侨,地道東北人纽谒。 一個(gè)月前我還...
    沈念sama閱讀 49,299評(píng)論 3 379
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像如输,于是被迫代替她去往敵國(guó)和親鼓黔。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,870評(píng)論 2 361

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