響應(yīng)式編程范式是怎么來的?

狀態(tài)變化需要響應(yīng),這種響應(yīng)有時候是連鎖反應(yīng)。具體來看一個傳統(tǒng)的 HTML 表單界面,相信上網(wǎng)時間長一點(diǎn)的應(yīng)該都很熟悉了:


古老的網(wǎng)頁登錄界面

這個簡單的表單,要做得好用了,變化響應(yīng)情況并不簡單:
1. 沒有輸入之前,兩個按鈕都應(yīng)該灰掉。
2. 任何一個輸入框有輸入后,Reset 按鈕都被激活。
3. 兩個輸入框都有輸入內(nèi)容之后,Submit 按鈕被激活。
4. 任何一個輸入框變空時,Submit 按鈕要灰掉。
5. Reset 被點(diǎn)擊,清除兩個輸入框的內(nèi)容,連鎖[1]。
6. Submit 被點(diǎn)擊,對每個輸入進(jìn)行有效性檢查
a. 全部有效,保存,并前往下一個界面。
b. 任何一個輸入無效,將相應(yīng)的輸入框變成紅色背景,并在其右側(cè)顯示錯誤信息。
其實(shí)還可做得更好,比如在輸入時,立即進(jìn)行有效性檢查,彈出自動完成提示等等。

當(dāng)然,要先知道有變化了,才談得上響應(yīng)。通常我們可以主動查詢狀態(tài)(輪詢),也可注冊到變化源上等通知(回調(diào)),還可以用一個中間組件專門處理注冊與通知派發(fā)(事件分發(fā))。

回調(diào)用得比較廣泛,寫起也直接,但是在有連鎖時邏輯會很零散,有依賴關(guān)系時也需要主動查詢,示意如下:

firstName.onChange : 
    if lastName not empty, submit.enable() 
    else if self is empty, submit.disable();
lastName.onChange :  
    if firstName not empty, submit.enable() 
    else
        if self is empty, submit.disable();
reset.onClick : 
    firstName.clear();
    lastName.clear(); 

這種零散,在程序略微有點(diǎn)長之后,就是一個很大的心智負(fù)擔(dān),修改規(guī)則時也非常容易錯漏??紤]到變化響應(yīng)邏輯實(shí)際上有兩部分:業(yè)務(wù)及狀態(tài)修改,把新狀態(tài)反映到界面上;有一種改進(jìn)方法,是后面部分集中起來。比如:

firstName.onChange : updateUI();
lastName.onChange :  updateUI();
reset.onClick : resetFields(); updateUI();

resetFields:
    firstName.clear();
    lastName.clear();
updateUI :
    if lastName is empty, 
        if firstName is empty, submit.disable()
    else 
        submit.enable()

輪循比較難安排響應(yīng)的時間點(diǎn),沒變化時也要轉(zhuǎn)有點(diǎn)浪費(fèi),比如:
timer.on(500ms, updateUI);
不過在大量頻繁變化的情況下,可以省掉派遣、回調(diào)的開銷,保持穩(wěn)定的性能。

如果有事件派發(fā)組件的話,程序可能是這樣的:

on textfield-change (src) : updateUI();
on button-click(src) : 
    if ( src is submit ) submit(); 
    if ( src is reset ) resetFields();
    updateUI();

不過,總是逃不掉寫那個 updateUI(),如果界面復(fù)雜了,還是一樣煩人的。其實(shí), 真正有書寫價值的只有兩條:

submit.enabled = (firstName not empty) && (lastName not empty);
reset.click => resetFields();

如果給事件派發(fā)組件再加點(diǎn)功能,其實(shí)可以省掉自己去處理 onChange 的:

dispatcher.connect( src: [firstName.text, lastName.text], 
                 target: rest.enabled, 
                   expr:{...} );
dispatcher.on( event: click, src: reset, do: resetFields );

connect()一種可能的實(shí)現(xiàn)辦法是:當(dāng) TextField 在編輯時,它內(nèi)部會不停的修改 .text 屬性,而 dispatcher 把自己注冊成 firstName 和 lastName 兩個對象的 text 屬性的觀察者,此時它被觸發(fā),執(zhí)行 expr 并將結(jié)果值賦給 target 屬性。

對于這種實(shí)現(xiàn)方法做一點(diǎn)拔高,我們可以認(rèn)為:
1. 編寫變化響應(yīng)代碼更聚焦在屬性和規(guī)則上了,也就變得直接。
2. 框架提供的機(jī)制替換每次要寫的膠水代碼,省事兒了。
3. 概念上,屬性變化要可被觀測。

能告知自己有變化的元素,取個名字叫 Observable;要收到變化通知進(jìn)行處理的,取個名字叫 Observer。觀察者會自動在觀測對象變化時被觸發(fā),這就是響應(yīng)式(Reactive)編程了

考慮更復(fù)雜的情況,

一個Houdini SOP場景

(from https://sites.google.com/site/fujitarium/Houdini/sop/vdb)
這是 sidefx Houdini 的一個場景,通過右側(cè)的網(wǎng)絡(luò)定義出左側(cè)圖形效果,網(wǎng)絡(luò)上的一個節(jié)點(diǎn)稱為一個 OP,節(jié)點(diǎn)上下的凸塊是輸入、輸出屬性,連接線表示輸出起點(diǎn)OP的屬性值給終點(diǎn)屬性。如果調(diào)整一下上圖中比較靠上的節(jié)點(diǎn),下游的節(jié)點(diǎn)都會受到影響,最終左側(cè)的結(jié)果也會立即改變。

對應(yīng)這個場景的代碼,如果能像下面這么寫:

(platonic1.out -> convert1.in);
(convert1.out |-> vdbfrompolygons1.in1
              |-> scatter1.in);
(vdbfrompolygons1.out -> vdbfracturel.in1
(sphere1.out -> vdbfracturel.in2);
(scatter1.out -> vdbfracturel.in3);
(vdbfracturel.out -> convertvdb1.in);

然后,platonic1 的運(yùn)算結(jié)果如果有變化,就會自動逐級傳下去,最生成正確的結(jié)果,是不是很爽?

這種變化影響傳播的鏈條,顯然可以看作一種流(Stream)。而函數(shù)式編程范式里,將函數(shù)做為數(shù)據(jù)進(jìn)行流動,是其實(shí)程序邏輯的基礎(chǔ)。從1980年代的 SISAL 語言開始,函數(shù)式語言研究者并已經(jīng)把這個模式下實(shí)現(xiàn)全部程序邏輯需要的流操作,很好的抽象整理了一套出來:map/filter/reduce。Reactive 的響應(yīng)式,加上 Functional 的流操作,這就是 Functional Reactive Programming (FRP) 啦。

FRP 的精髓就在于,框架替開發(fā)者寫膠水代碼,將變化響應(yīng)、傳遞表達(dá)得更直接。所以,框架一般還得帶一個執(zhí)行調(diào)度器,不然為了把多個異步活動串進(jìn)響應(yīng)流里,還得寫異步任務(wù)調(diào)度的膠水代碼。這就是現(xiàn)在最熱鬧的 Rx 系列啦。

看上去 RxAndroid 有點(diǎn)重,Agera 就輕量多了。一直不喜歡 ReactiveCocoa/RxSwift 的實(shí)現(xiàn),RxJS 還不錯。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • Spring Cloud為開發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見模式的工具(例如配置管理,服務(wù)發(fā)現(xiàn),斷路器,智...
    卡卡羅2017閱讀 136,534評論 19 139
  • 何為響應(yīng)式編程 響應(yīng)式編程是一種面向數(shù)據(jù)流和變化傳播的編程范式,數(shù)據(jù)更新是相關(guān)聯(lián)的。 這意味著可以在編程語言中很方...
    JasonDing閱讀 2,115評論 0 6
  • 發(fā)現(xiàn) 關(guān)注 消息 iOS 第三方庫、插件、知名博客總結(jié) 作者大灰狼的小綿羊哥哥關(guān)注 2017.06.26 09:4...
    肇東周閱讀 15,160評論 4 61
  • 移動設(shè)備: 微信:
    ainiok閱讀 698評論 0 2
  • 屈原活著的時候,是楚國的大V,是意見領(lǐng)袖, 總是和楚國當(dāng)局唱反調(diào)。 楚懷王說要團(tuán)結(jié)秦國, 屈原是反秦黨,看外交如此...
    Kanple閱讀 446評論 0 0

友情鏈接更多精彩內(nèi)容