欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

淺談Rx響應(yīng)式編程

 更新時(shí)間:2021年06月18日 11:16:12   作者:vivo互聯(lián)網(wǎng)技術(shù)  
在學(xué)習(xí)Rx編程的過(guò)程中,理解Observable這個(gè)概念至關(guān)重要,常規(guī)學(xué)習(xí)過(guò)程中,通常需要進(jìn)行多次碰壁才能逐漸開(kāi)悟。這個(gè)有點(diǎn)像小時(shí)候?qū)W騎自行車(chē),必須摔幾次才能掌握一樣。當(dāng)然如果有辦法能言傳,則可以少走一些彎路,盡快領(lǐng)悟Rx的精妙

一、Observable

Observable從字面翻譯來(lái)說(shuō)叫做“可觀察者”,換言之就是某種“數(shù)據(jù)源”或者“事件源”,這種數(shù)據(jù)源具有可被觀察的能力,這個(gè)和你主動(dòng)去撈數(shù)據(jù)有本質(zhì)區(qū)別。用一個(gè)形象的比喻就是Observable好比是水龍頭,你可以去打開(kāi)水龍頭——訂閱Observable,然后水——數(shù)據(jù)就會(huì)源源不斷流出。這就是響應(yīng)式編程的核心思想——變主動(dòng)為被動(dòng)。不過(guò)這個(gè)不在本篇文章中詳解。

Observable是一種概念,可以通過(guò)不同的方式去具體實(shí)現(xiàn),本文通過(guò)高階函數(shù)來(lái)實(shí)現(xiàn)兩個(gè)常用Observable:fromEvent和Interval。通過(guò)講解對(duì)Observable的訂閱和取消訂閱兩個(gè)行為來(lái)幫助讀者真正理解Observable是什么。

二、高階函數(shù)

高階函數(shù)的概念來(lái)源于函數(shù)式編程,簡(jiǎn)單的定義就是一個(gè)函數(shù)的入?yún)⒒蛘叻祷刂凳且粋€(gè)函數(shù)的函數(shù)。例如:

function foo(arg){
    return function(){
        console.log(arg)
    }
}
const bar = foo(“hello world”)
bar()  // hello world

ps:高階函數(shù)能做的事情很多,這里僅僅針對(duì)本文需要的情形進(jìn)行使用。

上面這個(gè)foo函數(shù)的調(diào)用并不會(huì)直接打印hello world,而只是把這個(gè)hello world給緩存起來(lái)。后面我們根據(jù)實(shí)際需要調(diào)用返回出來(lái)的bar函數(shù),然后真正去執(zhí)行打印hello world的工作。

為啥要做這么一步封裝呢?實(shí)際上這么做的效果就是“延遲”了調(diào)用。而一切的精髓就在這個(gè)“延遲”兩個(gè)字里面。我們實(shí)際上是對(duì)一種行為進(jìn)行了包裝,看上去就像某種一致的東西,好比是快遞盒子。

里面可以裝不同的東西,但對(duì)于物流來(lái)說(shuō)就是統(tǒng)一的東西。因此,就可以形成對(duì)快遞盒的統(tǒng)一操作,比如堆疊、運(yùn)輸、存儲(chǔ)、甚至是打開(kāi)盒子這個(gè)動(dòng)作也是一致的。

回到前面的例子,調(diào)用foo函數(shù),相當(dāng)于打包了一個(gè)快遞盒,這個(gè)快遞盒里面有一個(gè)固定的程序,就是當(dāng)打開(kāi)這個(gè)快遞盒(調(diào)用bar)時(shí)執(zhí)行一個(gè)打印操作。

我們可以有foo1、foo2、foo3……里面有各種各樣的程序,但是這些foos,都有一個(gè)共同的操作就是“打開(kāi)”。(前提是這個(gè)foo會(huì)返回一個(gè)函數(shù),這樣才能滿足“打開(kāi)”的操作,即調(diào)用返回的函數(shù))。

function foo1(arg){
    return function(){
       console.log(arg+"?")
    }
}
function foo2(arg){
      return function(){
         console.log(arg+"!")
     }
}
const bar1 = foo1(“hello world”)
const bar2 = foo2("yes")
bar1()+bar2() // hello world? yes!

三、快遞盒模型

3.1、快遞盒模型1:fromEvent

有了上面的基礎(chǔ),下面我們就來(lái)看一下Rx編程中最常用的一個(gè)Observable—fromEvent(……)。對(duì)于Rx編程的初學(xué)者,起初很難理解fromEvent(……)和addEventListener(……)有什么區(qū)別。

btn.addEventListener("click",callback)
rx.fromEvent(btn,"click").subscribe(callback)

如果直接執(zhí)行這個(gè)代碼,確實(shí)效果是一樣的。那么區(qū)別在哪兒呢?最直接的區(qū)別是,subscribe函數(shù)作用在fromEvent(……)上而不是btn上,而addEventListener是直接作用在btn上的。subscribe函數(shù)是某種“打開(kāi)”操作,而fromEvent(……)則是某種快遞盒。

fromEvent實(shí)際上是對(duì)addEventListener的“延遲”調(diào)用

function fromEvent(target,evtName){
    return function(callback){
        target.addEventListener(evtName,callback)
    }
}
const ob = fromEvent(btn,"click")
ob(console.log)// 相當(dāng)于 subscribe

哦!fromEvent本質(zhì)上是高階函數(shù)

至于如何實(shí)現(xiàn)subscribe來(lái)完成“打開(kāi)”操作,不在本文討論范圍,在Rx編程中,這個(gè)subscribe的動(dòng)作叫做“訂閱”?!坝嗛啞本褪撬蠴bservable的統(tǒng)一具備的操作。再次強(qiáng)調(diào):本文中對(duì)Observable的“調(diào)用”在邏輯上相當(dāng)于subscribe。

下面再舉一個(gè)例子,基本可以讓讀者舉二反N了。

3.2、快遞盒模型2:interval

Rx中有一個(gè)interval,它和setInterval有什么區(qū)別呢?

估計(jì)有人已經(jīng)開(kāi)始搶答了,interval就是對(duì)setInterval的延遲調(diào)用!bingo!

function interval(period){
    let i = 0
    return function(callback){
        setInterval(period,()=>callback(i++))
    }
}
const ob = interval(1000)
ob(console.log)// 相當(dāng)于 subscribe

從上面兩個(gè)例子來(lái)看,無(wú)論是fromEvent(……)還是Interval(……),雖然內(nèi)部是完全不同的邏輯,但是他們同屬于“快遞盒”這種東西,我們把它稱之為Observable——可觀察者。

fromEvent和Interval本身只是制作“快遞盒”的模型,只有調(diào)用后返回的東西才是“快遞盒”,即fromEvent(btn,"click")、interval(1000) 等等...

四、高階快遞盒

有了上面的基礎(chǔ),下面開(kāi)始進(jìn)階:我們擁有了那么多快遞盒,那么就可以對(duì)這些快遞盒再封裝。

在文章開(kāi)頭說(shuō)了,快遞盒統(tǒng)一了一些操作,所以我們可以把許許多多的快遞盒堆疊在一起,即組合成一個(gè)大的快遞盒!這個(gè)大的快遞盒和小的快遞盒一樣,具有“打開(kāi)”操作(即訂閱)。當(dāng)我們打開(kāi)這個(gè)大的快遞盒的時(shí)候,會(huì)發(fā)生什么呢?

可以有很多種不同的可能性,比如可以逐個(gè)打開(kāi)小的快遞盒(concat),或者一次性打開(kāi)所有小的快遞盒(merge),也可以只打開(kāi)那個(gè)最容易打開(kāi)的快遞盒(race)。

下面是一個(gè)簡(jiǎn)化版的merge方法:

function merge(...obs){
    return function(callback){
        obs.forEach(ob=>ob(callback)) // 打開(kāi)所有快遞盒
    }
}

我們還是拿之前的fromEvent和interval來(lái)舉例吧!

使用merge方法對(duì)兩個(gè)Observable進(jìn)行組合:

const ob1 = fromEvent(btn,'click') // 制作快遞盒1
const ob2 = interval(1000) // 制作快遞盒2
const ob = merge(ob1,ob2) //制作大快遞盒
ob(console.log) // 打開(kāi)大快遞盒

當(dāng)我們“打開(kāi)”(訂閱)這個(gè)大快遞盒ob的時(shí)候,其中兩個(gè)小快遞盒也會(huì)被“打開(kāi)”(訂閱),任意一個(gè)小快遞盒里面的邏輯都會(huì)被執(zhí)行,我們就合并(merge)了兩個(gè)Observable,變成了一個(gè)。

這就是我們?yōu)槭裁匆列量嗫喟迅鞣N異步函數(shù)封裝成快遞盒(Observable)的原因了——方便對(duì)他們進(jìn)行統(tǒng)一操作!當(dāng)然僅僅只是“打開(kāi)”(訂閱)這個(gè)操作只是最初級(jí)的功能,下面開(kāi)始進(jìn)階。

五、銷(xiāo)毀快遞盒

5.1、銷(xiāo)毀快遞盒——取消訂閱

我們還是以fromEvent為例子,之前我們寫(xiě)了一個(gè)簡(jiǎn)單的高階函數(shù),作為對(duì)addEventListener的封裝:

function fromEvent(target,evtName){
    return function(callback){
        target.addEventListener(evtName,callback)
    }
}

當(dāng)我們調(diào)用這個(gè)函數(shù)的時(shí)候,就生成了一個(gè)快遞盒(fromEvent(btn,'click'))。當(dāng)我們調(diào)用了這個(gè)函數(shù)返回的函數(shù)的時(shí)候,就是打開(kāi)了快遞盒(fromEvent(btn,'click')(console.log))。

那么我們?cè)趺慈ヤN(xiāo)毀這個(gè)打開(kāi)的快遞盒呢?

首先我們需要得到一個(gè)已經(jīng)打開(kāi)的快遞盒,上面的函數(shù)調(diào)用結(jié)果是void,我們無(wú)法做任何操作,所以我們需要構(gòu)造出一個(gè)打開(kāi)狀態(tài)的快遞盒。還是使用高階函數(shù)的思想:在返回的函數(shù)里面再返回一個(gè)函數(shù),用于銷(xiāo)毀操作。

function fromEvent(target,evtName){
    return function(callback){
        target.addEventListener(evtName,callback)
        return function(){
            target.removeEventListener(evtName,callback)
        }
    }
}
const ob = fromEvent(btn,'click') // 制作快遞盒
const sub = ob(console.log) // 打開(kāi)快遞盒,并得到一個(gè)可用于銷(xiāo)毀的函數(shù)
sub() // 銷(xiāo)毀快遞盒

同理,對(duì)于interval,我們也可以如法炮制:

function interval(period){
    let i = 0
    return function(callback){
        let id = setInterval(period,()=>callback(i++))
        return function(){
            clearInterval(id)
        }
    }
}
const ob = interval(1000) // 制作快遞盒
const sub = ob(console.log) // 打開(kāi)快遞盒
sub() // 銷(xiāo)毀快遞盒

5.2、銷(xiāo)毀高階快遞盒

我們以merge為例:

function merge(...obs){
    return function(callback){
        const subs = obs.map(ob=>ob(callback)) // 訂閱所有并收集所有的銷(xiāo)毀函數(shù)
        return function(){
            subs.forEach(sub=>sub()) // 遍歷銷(xiāo)毀函數(shù)并執(zhí)行
        }
    }
}
 
const ob1 = fromEvent(btn,'click') // 制作快遞盒1
const ob2 = interval(1000) // 制作快遞盒2
const ob = merge(ob1,ob2) //制作大快遞盒
const sub = ob(console.log) // 打開(kāi)大快遞盒
sub() // 銷(xiāo)毀大快遞盒

當(dāng)我們銷(xiāo)毀大快遞盒的時(shí)候,就會(huì)把里面所有的小快遞盒一起銷(xiāo)毀。

六、補(bǔ)充

到這里我們已經(jīng)將Observable的兩個(gè)重要操作(訂閱、取消訂閱)講完了,值得注意的是,取消訂閱這個(gè)行為并非是作用于Observable上,而是作用于已經(jīng)“打開(kāi)”的快遞盒(訂閱Observable后返回的東西)之上!

Observable除此以外,還有兩個(gè)重要操作,即發(fā)出事件、完成/異常,(這兩個(gè)操作屬于是由Observable主動(dòng)發(fā)起的回調(diào),和操作的方向是相反的,所以其實(shí)不能稱之為操作)。

這個(gè)兩個(gè)行為用快遞盒就不那么形象了,我們可以將Observable比做是水龍頭,原先的打開(kāi)快遞盒變成擰開(kāi)水龍頭,而我們傳入的回調(diào)函數(shù)就可以比喻成接水的水杯!由于大家對(duì)回調(diào)函數(shù)已經(jīng)非常熟悉了,所以本文就不再贅述了。

七、后記

總結(jié)一下我們學(xué)習(xí)的內(nèi)容,我們通過(guò)高階函數(shù)將一些操作進(jìn)行了“延遲”,并賦予了統(tǒng)一的行為,比如“訂閱”就是延遲執(zhí)行了異步函數(shù),“取消訂閱”就是在上面的基礎(chǔ)上再“延遲”執(zhí)行了銷(xiāo)毀資源的函數(shù)。

這些所謂的“延遲”執(zhí)行就是Rx編程中幕后最難理解,也是最核心的部分。Rx的本質(zhì)就是將異步函數(shù)封裝起來(lái),然后抽象成四大行為:訂閱、取消訂閱、發(fā)出事件、完成/異常。

實(shí)際實(shí)現(xiàn)Rx庫(kù)的方法有很多,本文只是利用了高階函數(shù)的思想來(lái)幫助大家理解Observable的本質(zhì),在官方實(shí)現(xiàn)的版本中,Observable這個(gè)快遞盒并非是高階函數(shù),而是一個(gè)對(duì)象,但本質(zhì)上是一樣的

以上就是淺談Rx響應(yīng)式編程的詳細(xì)內(nèi)容,更多關(guān)于Rx響應(yīng)式編程的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • 小程序外賣(mài)訂單界面的示例代碼

    小程序外賣(mài)訂單界面的示例代碼

    這篇文章主要介紹了小程序外賣(mài)訂單界面的示例代碼,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2019-12-12
  • 你可能不知道的typescript實(shí)用小技巧

    你可能不知道的typescript實(shí)用小技巧

    作為前端程序員,TS已經(jīng)成為一項(xiàng)必不可少的技能,本文旨在介紹 TS中的一些實(shí)用技巧,提高大家對(duì)這門(mén)語(yǔ)言更深的認(rèn)知,這篇文章主要給大家介紹了關(guān)于typescript實(shí)用小技巧的相關(guān)資料,需要的朋友可以參考下
    2021-08-08
  • JavaScript中添加監(jiān)聽(tīng)句柄的方式

    JavaScript中添加監(jiān)聽(tīng)句柄的方式

    這篇文章主要介紹了JavaScript中添加監(jiān)聽(tīng)句柄的方式,監(jiān)聽(tīng)就是觸發(fā)某事件之后做出的響應(yīng),監(jiān)聽(tīng)句柄是觸發(fā)某相應(yīng)的條件,下面關(guān)于添加監(jiān)聽(tīng)句柄的方式的詳細(xì)內(nèi)容,需要的朋友可以參考一下,希望對(duì)你有所幫助
    2022-02-02
  • JavaScript中的子窗口與父窗口的互相調(diào)用問(wèn)題

    JavaScript中的子窗口與父窗口的互相調(diào)用問(wèn)題

    本文給大家介紹了JavaScript中的子窗口與父窗口的互相調(diào)用問(wèn)題,非常不錯(cuò),具有參考借鑒價(jià)值,需要的朋友參考下吧
    2017-02-02
  • JavaScript不刷新實(shí)現(xiàn)瀏覽器的前進(jìn)后退功能

    JavaScript不刷新實(shí)現(xiàn)瀏覽器的前進(jìn)后退功能

    這篇文章主要介紹了JavaScript不刷新實(shí)現(xiàn)瀏覽器的前進(jìn)后退功能,本文給出了HTML5解決方案、老舊瀏覽器的寫(xiě)法等方法,需要的朋友可以參考下
    2014-11-11
  • 使用JS前端加密庫(kù)sm-crypto實(shí)現(xiàn)國(guó)密sm2、sm3和sm4加密與解密

    使用JS前端加密庫(kù)sm-crypto實(shí)現(xiàn)國(guó)密sm2、sm3和sm4加密與解密

    這篇文章主要介紹了使用JS前端加密庫(kù)sm-crypto實(shí)現(xiàn)國(guó)密sm2、sm3和sm4加密與解密,需要的朋友可以參考下
    2024-06-06
  • 解決layui表格的表頭不滾動(dòng)的問(wèn)題

    解決layui表格的表頭不滾動(dòng)的問(wèn)題

    今天小編就為大家分享一篇解決layui表格的表頭不滾動(dòng)的問(wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧
    2019-09-09
  • 微信小程序?qū)崿F(xiàn)簡(jiǎn)單的搖骰子游戲

    微信小程序?qū)崿F(xiàn)簡(jiǎn)單的搖骰子游戲

    這篇文章主要為大家詳細(xì)介紹了微信小程序?qū)崿F(xiàn)簡(jiǎn)單的搖骰子游戲,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2021-05-05
  • JavaScript實(shí)現(xiàn)日期格式化詳細(xì)實(shí)例

    JavaScript實(shí)現(xiàn)日期格式化詳細(xì)實(shí)例

    這篇文章主要給大家介紹了關(guān)于JavaScript實(shí)現(xiàn)日期格式化的相關(guān)資料,JavaScript中的日期對(duì)象提供了許多方法和屬性,可以通過(guò)它們來(lái)進(jìn)行日期的格式化,需要的朋友可以參考下
    2023-09-09
  • JS實(shí)現(xiàn)跟隨鼠標(biāo)的鏈接文字提示框效果

    JS實(shí)現(xiàn)跟隨鼠標(biāo)的鏈接文字提示框效果

    這篇文章主要介紹了JS實(shí)現(xiàn)跟隨鼠標(biāo)的鏈接文字提示框效果,涉及javascript鼠標(biāo)事件及頁(yè)面元素樣式操作的相關(guān)技巧,非常簡(jiǎn)單實(shí)用,需要的朋友可以參考下
    2015-08-08

最新評(píng)論